From 0209129618e63f5ca6d22bc9dd283f7735fb3466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 5 Mar 2025 12:25:57 +0100 Subject: [PATCH 01/58] Extract leaderboard fetch logic from song select beatmap leaderboard drawable RFC. Another attempt at this. - Supersedes https://github.com/ppy/osu/pull/31881 - Supersedes / closes https://github.com/ppy/osu/pull/31355 - Closes https://github.com/ppy/osu/issues/29861 This is a weird diff because I am feeling rather boxed in by all the constraints, namely that: - Leaderboard state should be global state - But the global state is essentially managed by song select and namely `BeatmapLeaderboard` itself. That's because trying to e.g. not have `BeatmapLeaderboard` pass the beatmap and the ruleset to the global leaderboard manager is worse, as it essentially introduces two parallel paths of execution that need to be somehow merged into one (as in I'd have to somehow sync `LeaderboardManager` responding to beatmap/ruleset changes with `BeatmapLeaderboard` which is inheritance hell) - Also local leaderboard fetching is data-push (as in the scores can change under the leaderboard manager), and online leaderboard fetching is data-pull (as in the scores do not change unless the leaderboard manager does something). Also online leaderboard fetching can fail. Which is why I need to still have the weird setup wherein there's a `FetchWithCriteriaAsync()` (because I need to be able to respond to online requests taking time, or failing), but also the `BeatmapLeaderboard` only uses the public `Scores` bindable to actually read the scores (because it needs to respond to new local scores arriving). - Another thing to think about here is what happens when a retrieval fails because e.g. the user requested friend leaderboards without having supporter. With how this diff is written, that special condition is handled to `BeatmapLeaderboard`, and `LeaderboardManager`'s state will remain as whatever it was before that scope change was requested, which may be considered good or it may not (I imagine it's better to show scores in gameplay than not in this case, but maybe I'm wrong?) --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 3 + .../Online/Leaderboards/LeaderboardManager.cs | 162 ++++++++++++++++++ osu.Game/OsuGameBase.cs | 5 + .../Select/Leaderboards/BeatmapLeaderboard.cs | 120 +++---------- 4 files changed, 192 insertions(+), 98 deletions(-) create mode 100644 osu.Game/Online/Leaderboards/LeaderboardManager.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 474d2ee6e3..ebeba23123 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect private RulesetStore rulesetStore = null!; private BeatmapManager beatmapManager = null!; private PlaySongSelect songSelect = null!; + private LeaderboardManager leaderboardManager = null!; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { @@ -52,6 +53,7 @@ namespace osu.Game.Tests.Visual.SongSelect dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API)); dependencies.CacheAs(songSelect = new PlaySongSelect()); Dependencies.Cache(Realm); + dependencies.Cache(leaderboardManager = new LeaderboardManager()); return dependencies; } @@ -60,6 +62,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void load() { LoadComponent(songSelect); + LoadComponent(leaderboardManager); } public TestSceneBeatmapLeaderboard() diff --git a/osu.Game/Online/Leaderboards/LeaderboardManager.cs b/osu.Game/Online/Leaderboards/LeaderboardManager.cs new file mode 100644 index 0000000000..9104c83c02 --- /dev/null +++ b/osu.Game/Online/Leaderboards/LeaderboardManager.cs @@ -0,0 +1,162 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using Realms; + +namespace osu.Game.Online.Leaderboards +{ + public partial class LeaderboardManager : Component + { + public IBindable Scores => scores; + private readonly Bindable scores = new Bindable(); + + private LeaderboardCriteria? criteria; + + private IDisposable? localScoreSubscription; + private TaskCompletionSource? localFetchCompletionSource; + private TaskCompletionSource? lastFetchCompletionSource; + private GetScoresRequest? inFlightOnlineRequest; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + [Resolved] + private RealmAccess realm { get; set; } = null!; + + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + public LeaderboardManager() + { + scores.BindValueChanged(_ => + { + if (localFetchCompletionSource != null && localFetchCompletionSource == lastFetchCompletionSource && scores.Value != null) + { + localFetchCompletionSource.SetResult(scores.Value); + localFetchCompletionSource = null; + } + }); + } + + public Task FetchWithCriteriaAsync(LeaderboardCriteria newCriteria) + { + if (criteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false) + return lastFetchCompletionSource?.Task ?? Task.FromResult(Scores.Value); + + criteria = newCriteria; + localScoreSubscription?.Dispose(); + inFlightOnlineRequest?.Cancel(); + lastFetchCompletionSource?.TrySetCanceled(); + scores.Value = null; + + switch (newCriteria.Scope) + { + case BeatmapLeaderboardScope.Local: + { + lastFetchCompletionSource = localFetchCompletionSource = new TaskCompletionSource(); + localScoreSubscription = realm.RegisterForNotifications(r => + r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" + + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" + + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" + + $" AND {nameof(ScoreInfo.DeletePending)} == false" + , newCriteria.Beatmap.ID, newCriteria.Ruleset.ShortName), localScoresChanged); + return localFetchCompletionSource.Task; + } + + default: + { + var onlineFetchCompletionSource = new TaskCompletionSource(); + lastFetchCompletionSource = onlineFetchCompletionSource; + + IReadOnlyList? requestMods = null; + + if (newCriteria.ExactMods != null) + { + if (!newCriteria.ExactMods.Any()) + // add nomod for the request + requestMods = new Mod[] { new ModNoMod() }; + else + requestMods = newCriteria.ExactMods; + } + + var newRequest = new GetScoresRequest(newCriteria.Beatmap, newCriteria.Ruleset, newCriteria.Scope, requestMods); + newRequest.Success += response => + { + if (inFlightOnlineRequest != null && !newRequest.Equals(inFlightOnlineRequest)) + return; + + var result = new LeaderboardScores + ( + response.Scores.Select(s => s.ToScoreInfo(rulesets, newCriteria.Beatmap)).OrderByTotalScore(), + response.UserScore?.CreateScoreInfo(rulesets, newCriteria.Beatmap) + ); + inFlightOnlineRequest = null; + if (onlineFetchCompletionSource.TrySetResult(result)) + scores.Value = result; + }; + newRequest.Failure += ex => onlineFetchCompletionSource.TrySetException(ex); + api.Queue(inFlightOnlineRequest = newRequest); + return onlineFetchCompletionSource.Task; + } + } + } + + private void localScoresChanged(IRealmCollection sender, ChangeSet? changes) + { + Debug.Assert(criteria != null); + + // This subscription may fire from changes to linked beatmaps, which we don't care about. + // It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications. + if (changes?.HasCollectionChanges() == false) + return; + + var newScores = sender.AsEnumerable(); + + if (criteria.ExactMods != null) + { + if (!criteria.ExactMods.Any()) + { + // we need to filter out all scores that have any mods to get all local nomod scores + newScores = newScores.Where(s => !s.Mods.Any()); + } + else + { + // otherwise find all the scores that have all of the currently selected mods (similar to how web applies mod filters) + // we're creating and using a string HashSet representation of selected mods so that it can be translated into the DB query itself + var selectedMods = criteria.ExactMods.Select(m => m.Acronym).ToHashSet(); + + newScores = newScores.Where(s => selectedMods.SetEquals(s.Mods.Select(m => m.Acronym))); + } + } + + newScores = newScores.Detach().OrderByTotalScore(); + + scores.Value = new LeaderboardScores(newScores, null); + } + } + + public record LeaderboardCriteria( + BeatmapInfo Beatmap, + RulesetInfo Ruleset, + BeatmapLeaderboardScope Scope, + Mod[]? ExactMods + ); + + public record LeaderboardScores(IEnumerable TopScores, ScoreInfo? UserScore); +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4087a8b71e..fb28b8c5a4 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -49,6 +49,7 @@ using osu.Game.Localisation; using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.Chat; +using osu.Game.Online.Leaderboards; using osu.Game.Online.Metadata; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; @@ -203,6 +204,7 @@ namespace osu.Game private UserLookupCache userCache; private BeatmapLookupCache beatmapCache; + private LeaderboardManager leaderboardManager; private RulesetConfigCache rulesetConfigCache; @@ -365,6 +367,9 @@ namespace osu.Game dependencies.CacheAs>(Beatmap); dependencies.CacheAs(Beatmap); + dependencies.Cache(leaderboardManager = new LeaderboardManager()); + base.Content.Add(leaderboardManager); + // add api components to hierarchy. if (API is APIAccess apiAccess) base.Content.Add(apiAccess); diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 46705aaa28..e435554b03 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -3,21 +3,17 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; -using osu.Game.Database; using osu.Game.Extensions; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using Realms; namespace osu.Game.Screens.Select.Leaderboards { @@ -67,6 +63,8 @@ namespace osu.Game.Screens.Select.Leaderboards } } + private readonly IBindable fetchedScores = new Bindable(); + [Resolved] private IBindable ruleset { get; set; } = null!; @@ -77,14 +75,7 @@ namespace osu.Game.Screens.Select.Leaderboards private IAPIProvider api { get; set; } = null!; [Resolved] - private RulesetStore rulesets { get; set; } = null!; - - [Resolved] - private RealmAccess realm { get; set; } = null!; - - private IDisposable? scoreSubscription; - - private GetScoresRequest? scoreRetrievalRequest; + private LeaderboardManager leaderboardManager { get; set; } = null!; [BackgroundDependencyLoader] private void load() @@ -95,15 +86,23 @@ namespace osu.Game.Screens.Select.Leaderboards if (filterMods) RefetchScores(); }; + fetchedScores.BindTo(leaderboardManager.Scores); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + fetchedScores.BindValueChanged(_ => + { + if (fetchedScores.Value != null) + SetScores(fetchedScores.Value.TopScores, fetchedScores.Value.UserScore); + }); } protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; protected override APIRequest? FetchScores(CancellationToken cancellationToken) { - scoreRetrievalRequest?.Cancel(); - scoreRetrievalRequest = null; - var fetchBeatmapInfo = BeatmapInfo; if (fetchBeatmapInfo == null) @@ -114,12 +113,6 @@ namespace osu.Game.Screens.Select.Leaderboards var fetchRuleset = ruleset.Value ?? fetchBeatmapInfo.Ruleset; - if (Scope == BeatmapLeaderboardScope.Local) - { - subscribeToLocalScores(fetchBeatmapInfo, cancellationToken); - return null; - } - if (!api.IsLoggedIn) { SetErrorState(LeaderboardState.NotLoggedIn); @@ -132,7 +125,7 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - if (fetchBeatmapInfo.OnlineID <= 0 || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending) + if ((fetchBeatmapInfo.OnlineID <= 0 || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending) && IsOnlineScope) { SetErrorState(LeaderboardState.BeatmapUnavailable); return null; @@ -150,29 +143,14 @@ namespace osu.Game.Screens.Select.Leaderboards return null; } - IReadOnlyList? requestMods = null; + leaderboardManager.FetchWithCriteriaAsync(new LeaderboardCriteria(fetchBeatmapInfo, fetchRuleset, Scope, filterMods ? mods.Value.ToArray() : null)) + .ContinueWith(t => + { + if (t.Exception != null && !t.IsCanceled) + Schedule(() => SetErrorState(LeaderboardState.NetworkFailure)); + }, cancellationToken); - if (filterMods && !mods.Value.Any()) - // add nomod for the request - requestMods = new Mod[] { new ModNoMod() }; - else if (filterMods) - requestMods = mods.Value; - - var newRequest = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods); - newRequest.Success += response => Schedule(() => - { - // Request may have changed since fetch request. - // Can't rely on request cancellation due to Schedule inside SetScores so let's play it safe. - if (!newRequest.Equals(scoreRetrievalRequest)) - return; - - SetScores( - response.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo)).OrderByTotalScore(), - response.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo) - ); - }); - - return scoreRetrievalRequest = newRequest; + return null; } protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index, IsOnlineScope, Scope != BeatmapLeaderboardScope.Friend) @@ -184,59 +162,5 @@ namespace osu.Game.Screens.Select.Leaderboards { Action = () => ScoreSelected?.Invoke(model) }; - - private void subscribeToLocalScores(BeatmapInfo beatmapInfo, CancellationToken cancellationToken) - { - Debug.Assert(beatmapInfo != null); - - scoreSubscription?.Dispose(); - scoreSubscription = null; - - scoreSubscription = realm.RegisterForNotifications(r => - r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" - + $" AND {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.Hash)} == {nameof(ScoreInfo.BeatmapHash)}" - + $" AND {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $1" - + $" AND {nameof(ScoreInfo.DeletePending)} == false" - , beatmapInfo.ID, ruleset.Value.ShortName), localScoresChanged); - - void localScoresChanged(IRealmCollection sender, ChangeSet? changes) - { - if (cancellationToken.IsCancellationRequested) - return; - - // This subscription may fire from changes to linked beatmaps, which we don't care about. - // It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications. - if (changes?.HasCollectionChanges() == false) - return; - - var scores = sender.AsEnumerable(); - - if (filterMods && !mods.Value.Any()) - { - // we need to filter out all scores that have any mods to get all local nomod scores - scores = scores.Where(s => !s.Mods.Any()); - } - else if (filterMods) - { - // otherwise find all the scores that have all of the currently selected mods (similar to how web applies mod filters) - // we're creating and using a string HashSet representation of selected mods so that it can be translated into the DB query itself - var selectedMods = mods.Value.Select(m => m.Acronym).ToHashSet(); - - scores = scores.Where(s => selectedMods.SetEquals(s.Mods.Select(m => m.Acronym))); - } - - scores = scores.Detach().OrderByTotalScore(); - - SetScores(scores); - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - scoreSubscription?.Dispose(); - scoreRetrievalRequest?.Cancel(); - } } } From f3d9f21e71da17ed4035fa1936f82cc0ebca320b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Apr 2025 13:02:14 +0200 Subject: [PATCH 02/58] Fix test failures --- osu.Game/Online/Leaderboards/LeaderboardManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardManager.cs b/osu.Game/Online/Leaderboards/LeaderboardManager.cs index 9104c83c02..4ca090f8a0 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardManager.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardManager.cs @@ -49,7 +49,7 @@ namespace osu.Game.Online.Leaderboards if (localFetchCompletionSource != null && localFetchCompletionSource == lastFetchCompletionSource && scores.Value != null) { localFetchCompletionSource.SetResult(scores.Value); - localFetchCompletionSource = null; + localFetchCompletionSource = lastFetchCompletionSource = null; } }); } From 9aaa1fe5f42a8749ab9493c279e11df62fb37526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Apr 2025 13:08:11 +0200 Subject: [PATCH 03/58] Simplify code further --- .../Online/Leaderboards/LeaderboardManager.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardManager.cs b/osu.Game/Online/Leaderboards/LeaderboardManager.cs index 4ca090f8a0..7ca5de6f21 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardManager.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardManager.cs @@ -42,18 +42,6 @@ namespace osu.Game.Online.Leaderboards [Resolved] private RulesetStore rulesets { get; set; } = null!; - public LeaderboardManager() - { - scores.BindValueChanged(_ => - { - if (localFetchCompletionSource != null && localFetchCompletionSource == lastFetchCompletionSource && scores.Value != null) - { - localFetchCompletionSource.SetResult(scores.Value); - localFetchCompletionSource = lastFetchCompletionSource = null; - } - }); - } - public Task FetchWithCriteriaAsync(LeaderboardCriteria newCriteria) { if (criteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false) @@ -69,6 +57,8 @@ namespace osu.Game.Online.Leaderboards { case BeatmapLeaderboardScope.Local: { + // this task completion source will be marked completed in the `localScoresChanged()` below. + // yes it's twisty, but such are the costs of trying to reconcile data-push / subscription and data-pull / explicit fetch flows. lastFetchCompletionSource = localFetchCompletionSource = new TaskCompletionSource(); localScoreSubscription = realm.RegisterForNotifications(r => r.All().Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $0" @@ -148,6 +138,12 @@ namespace osu.Game.Online.Leaderboards newScores = newScores.Detach().OrderByTotalScore(); scores.Value = new LeaderboardScores(newScores, null); + + if (localFetchCompletionSource != null && localFetchCompletionSource == lastFetchCompletionSource) + { + localFetchCompletionSource.SetResult(scores.Value); + localFetchCompletionSource = lastFetchCompletionSource = null; + } } } From 68b5f9a314b420ab520745fdf3ff12b1d2f28dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Apr 2025 13:35:24 +0200 Subject: [PATCH 04/58] Fix various weirdness around refetching --- .../Select/Leaderboards/BeatmapLeaderboard.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index e435554b03..c52cd61c42 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private readonly IBindable fetchedScores = new Bindable(); + private readonly Bindable fetchedScores = new Bindable(); [Resolved] private IBindable ruleset { get; set; } = null!; @@ -86,17 +86,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (filterMods) RefetchScores(); }; - fetchedScores.BindTo(leaderboardManager.Scores); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - fetchedScores.BindValueChanged(_ => - { - if (fetchedScores.Value != null) - SetScores(fetchedScores.Value.TopScores, fetchedScores.Value.UserScore); - }); + ((IBindable)fetchedScores).BindTo(leaderboardManager.Scores); } protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local; @@ -147,7 +137,17 @@ namespace osu.Game.Screens.Select.Leaderboards .ContinueWith(t => { if (t.Exception != null && !t.IsCanceled) + { Schedule(() => SetErrorState(LeaderboardState.NetworkFailure)); + return; + } + + fetchedScores.UnbindEvents(); + fetchedScores.BindValueChanged(scores => + { + if (scores.NewValue != null) + Schedule(() => SetScores(scores.NewValue.TopScores, scores.NewValue.UserScore)); + }, true); }, cancellationToken); return null; From 0c7c791fb5c40235c6033ddf1f68811f0e4325a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Apr 2025 19:34:01 +0900 Subject: [PATCH 05/58] Fix `StarRatingDisplay` not using fixed width text --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 4119ffb636..dbd4918c81 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -127,9 +127,8 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, Margin = new MarginPadding { Bottom = 1.5f }, - // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f - // see https://github.com/ppy/osu-framework/issues/3271. - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + Spacing = new Vector2(-1.4f), + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold, fixedWidth: true), Shadow = false, }, }, From 962edbdf78f7447776f03b8ea23da8aa6f52e7a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Apr 2025 01:46:44 +0900 Subject: [PATCH 06/58] Remove manual handling of autosize now that we are using fixed width display --- .../Beatmaps/Drawables/StarRatingDisplay.cs | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index dbd4918c81..9eea53d621 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -119,18 +119,14 @@ namespace osu.Game.Beatmaps.Drawables Size = new Vector2(8f), }, Empty(), - textContainer = new Container + starsText = new OsuSpriteText { - AutoSizeAxes = Axes.Y, - Child = starsText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 1.5f }, - Spacing = new Vector2(-1.4f), - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold, fixedWidth: true), - Shadow = false, - }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 1.5f }, + Spacing = new Vector2(-1.4f), + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold, fixedWidth: true), + Shadow = false, }, } } @@ -161,11 +157,6 @@ namespace osu.Game.Beatmaps.Drawables starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); - - // In order to avoid autosize throwing the width of these displays all over the place, - // let's lock in some sane defaults for the text width based on how many digits we're - // displaying. - textContainer.Width = 24 + Math.Max(starsText.Text.ToString().Length - 4, 0) * 6; }, true); } } From fb326c6afd0d0a8b6efd532af7a9fbcbbf62f52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 10 Apr 2025 07:47:31 +0200 Subject: [PATCH 07/58] Fix code quality --- osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index 9eea53d621..050a78a6b4 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -39,8 +38,6 @@ namespace osu.Game.Beatmaps.Drawables private readonly Bindable displayedStars = new BindableDouble(); - private readonly Container textContainer; - /// /// The currently displayed stars of this display wrapped in a bindable. /// This bindable gets transformed on change rather than instantaneous, if animation is enabled. From 92c0b3cae267b8d12c8e3e659cfc6e30b900938e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 10 Apr 2025 15:52:49 +0900 Subject: [PATCH 08/58] Fix progressively worsening performance on taiko argon skin --- .../Skinning/Argon/ArgonCirclePiece.cs | 9 +++++++++ osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs index cecb99c690..d94031380b 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonCirclePiece.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; @@ -112,5 +113,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableHitObject.IsNotNull()) + drawableHitObject.ApplyCustomUpdateState -= updateStateTransforms; + } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs index b3833d372c..a7b1b9c4b6 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Default/CirclePiece.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; @@ -202,5 +203,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default .Then() .FadeEdgeEffectTo(edge_alpha_kiai, duration, Easing.OutQuint); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableHitObject.IsNotNull()) + drawableHitObject.ApplyCustomUpdateState -= updateStateTransforms; + } } } From 05dc113c703a2aaa2c3f58adff0611ae38fc9fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 10 Apr 2025 08:24:53 +0200 Subject: [PATCH 09/58] Use more distinctive window title for tournament client This is in response to feedback in https://discord.com/channels/188630481301012481/1097318920991559880/1359117234257268747. The long and short of it is that without a unique title it's a bit hard to tell in software like OBS which window is the game and which window is the tournament client if wanting to run both, which I can agree with. --- osu.Game.Tournament/TournamentGameBase.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index eecd097a97..2be7c4aff3 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -53,6 +53,14 @@ namespace osu.Game.Tournament return new ProductionEndpointConfiguration(); } + public override void SetHost(GameHost host) + { + base.SetHost(host); + + if (host.Window != null) + host.Window.Title = $"{Name} [tournament client]"; + } + private TournamentSpriteText initialisationText = null!; [BackgroundDependencyLoader] From d0dddcde7847da8f0a16ca9e015efebf923a2321 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 10 Apr 2025 16:09:47 +0900 Subject: [PATCH 10/58] Fix another missing disposal This one probably doesn't matter as much because it's used as a single instance in `TaikoPlayfield` (so its lifetime should end around the same time as the `HealthProcessor`). --- .../Skinning/Legacy/LegacyKiaiGlow.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs index 9877efa127..58830f7492 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyKiaiGlow.cs @@ -17,12 +17,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { internal partial class LegacyKiaiGlow : BeatSyncedContainer { - private bool isKiaiActive; + [Resolved] + private HealthProcessor? healthProcessor { get; set; } + private bool isKiaiActive; private Sprite sprite = null!; - [BackgroundDependencyLoader(true)] - private void load(ISkinSource skin, HealthProcessor? healthProcessor) + [BackgroundDependencyLoader] + private void load(ISkinSource skin) { Child = sprite = new Sprite { @@ -33,6 +35,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy Scale = new Vector2(TaikoLegacyHitTarget.SCALE), Colour = new Colour4(255, 228, 0, 255), }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); if (healthProcessor != null) healthProcessor.NewJudgement += onNewJudgement; @@ -61,5 +68,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy sprite.ScaleTo(TaikoLegacyHitTarget.SCALE + 0.15f).Then() .ScaleTo(TaikoLegacyHitTarget.SCALE, 80, Easing.OutQuad); } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (healthProcessor != null) + healthProcessor.NewJudgement -= onNewJudgement; + } } } From 42ff312ece51e9956a3c539ac9ec9c4576059bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Apr 2025 13:53:07 +0200 Subject: [PATCH 11/58] Actually use leaderboard manager in players This was supposed to be the case all along, but I guess in all of the rewrite attempts I forgot? With this, https://github.com/ppy/osu/issues/29861 is actually fixed again - and additionally, so is https://github.com/ppy/osu/issues/26716. --- .../Online/Leaderboards/LeaderboardManager.cs | 16 ++++++++++- osu.Game/Screens/Play/ReplayPlayer.cs | 25 +++++++++++++++-- osu.Game/Screens/Play/SoloPlayer.cs | 28 ++++++++++++++++--- osu.Game/Screens/Select/PlaySongSelect.cs | 10 ++----- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardManager.cs b/osu.Game/Online/Leaderboards/LeaderboardManager.cs index 7ca5de6f21..314705eb02 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardManager.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardManager.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Rulesets; @@ -154,5 +155,18 @@ namespace osu.Game.Online.Leaderboards Mod[]? ExactMods ); - public record LeaderboardScores(IEnumerable TopScores, ScoreInfo? UserScore); + public record LeaderboardScores(IEnumerable TopScores, ScoreInfo? UserScore) + { + public IEnumerable AllScores + { + get + { + foreach (var score in TopScores) + yield return score; + + if (UserScore != null && TopScores.All(topScore => !topScore.Equals(UserScore) && !topScore.MatchesOnlineID(UserScore))) + yield return UserScore; + } + } + } } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index ba572f6014..a5952f3ff3 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Input.Bindings; +using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -59,6 +60,12 @@ namespace osu.Game.Screens.Play this.createScore = createScore; } + [Resolved] + private LeaderboardManager leaderboardManager { get; set; } = null!; + + private readonly IBindable globalScores = new Bindable(); + private readonly BindableList localScores = new BindableList(); + /// /// Add a settings group to the HUD overlay. Intended to be used by rulesets to add replay-specific settings. /// @@ -87,6 +94,20 @@ namespace osu.Game.Screens.Play HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings); } + protected override void LoadComplete() + { + base.LoadComplete(); + + globalScores.BindTo(leaderboardManager.Scores); + globalScores.BindValueChanged(_ => + { + localScores.Clear(); + + if (globalScores.Value is LeaderboardScores g) + localScores.AddRange(g.AllScores.OrderByTotalScore()); + }, true); + } + protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); @@ -97,13 +118,11 @@ namespace osu.Game.Screens.Play // Don't re-import replay scores as they're already present in the database. protected override Task ImportScore(Score score) => Task.CompletedTask; - public readonly BindableList LeaderboardScores = new BindableList(); - protected override GameplayLeaderboard CreateGameplayLeaderboard() => new SoloGameplayLeaderboard(Score.ScoreInfo.User) { AlwaysVisible = { Value = true }, - Scores = { BindTarget = LeaderboardScores } + Scores = { BindTarget = localScores } }; protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score) diff --git a/osu.Game/Screens/Play/SoloPlayer.cs b/osu.Game/Screens/Play/SoloPlayer.cs index f4cf2da364..ed5dea98cd 100644 --- a/osu.Game/Screens/Play/SoloPlayer.cs +++ b/osu.Game/Screens/Play/SoloPlayer.cs @@ -6,10 +6,12 @@ using System; using System.Diagnostics; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Online.API; +using osu.Game.Online.Leaderboards; using osu.Game.Online.Rooms; using osu.Game.Online.Solo; using osu.Game.Scoring; @@ -29,6 +31,26 @@ namespace osu.Game.Screens.Play { } + [Resolved] + private LeaderboardManager leaderboardManager { get; set; } = null!; + + private readonly IBindable globalScores = new Bindable(); + private readonly BindableList localScores = new BindableList(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + globalScores.BindTo(leaderboardManager.Scores); + globalScores.BindValueChanged(_ => + { + localScores.Clear(); + + if (globalScores.Value is LeaderboardScores g) + localScores.AddRange(g.AllScores.OrderByTotalScore()); + }, true); + } + protected override APIRequest CreateTokenRequest() { int beatmapId = Beatmap.Value.BeatmapInfo.OnlineID; @@ -43,13 +65,11 @@ namespace osu.Game.Screens.Play return new CreateSoloScoreRequest(Beatmap.Value.BeatmapInfo, rulesetId, Game.VersionHash); } - public readonly BindableList LeaderboardScores = new BindableList(); - protected override GameplayLeaderboard CreateGameplayLeaderboard() => new SoloGameplayLeaderboard(Score.ScoreInfo.User) { AlwaysVisible = { Value = false }, - Scores = { BindTarget = LeaderboardScores } + Scores = { BindTarget = localScores } }; protected override bool ShouldExitOnTokenRetrievalFailure(Exception exception) => false; @@ -59,7 +79,7 @@ namespace osu.Game.Screens.Play // Before importing a score, stop binding the leaderboard with its score source. // This avoids a case where the imported score may cause a leaderboard refresh // (if the leaderboard's source is local). - LeaderboardScores.UnbindBindings(); + globalScores.UnbindBindings(); return base.ImportScore(score); } diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 7b1479f392..c49b7c2ef2 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -129,17 +129,11 @@ namespace osu.Game.Screens.Select if (replayGeneratingMod != null) { - player = new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)) - { - LeaderboardScores = { BindTarget = playBeatmapDetailArea.Leaderboard.Scores } - }; + player = new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)); } else { - player = new SoloPlayer - { - LeaderboardScores = { BindTarget = playBeatmapDetailArea.Leaderboard.Scores } - }; + player = new SoloPlayer(); } return player; From 17bcc2842902a5befc90ce3249338493286d832b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 10 Apr 2025 09:45:25 +0200 Subject: [PATCH 12/58] Update scores request to move away from old endpoint --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index f2a2daccb5..ed26c77dd9 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -36,7 +36,7 @@ namespace osu.Game.Online.API.Requests this.mods = mods ?? Array.Empty(); } - protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/solo-scores{createQueryParameters()}"; + protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/scores{createQueryParameters()}"; private string createQueryParameters() { From 31b98ac7b99414870c6b02fb472d5ab39d64fa44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 10 Apr 2025 10:19:04 +0200 Subject: [PATCH 13/58] Ensure correct global leaderboard state when presenting scores With `ReplayPlayer` now consuming the `LeaderboardManager`'s global state, flows such as presenting a score need to set the global state up correctly to avoid accidentally showing a leaderboard from a completely different score. This also incidentally closes https://github.com/ppy/osu/issues/27609. --- .../Online/Leaderboards/LeaderboardManager.cs | 14 ++++++------- osu.Game/OsuGame.cs | 20 +++++++++++++++++++ osu.Game/OsuGameBase.cs | 6 +++--- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardManager.cs b/osu.Game/Online/Leaderboards/LeaderboardManager.cs index 314705eb02..ff3fe39a96 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardManager.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardManager.cs @@ -27,7 +27,7 @@ namespace osu.Game.Online.Leaderboards public IBindable Scores => scores; private readonly Bindable scores = new Bindable(); - private LeaderboardCriteria? criteria; + public LeaderboardCriteria? CurrentCriteria { get; private set; } private IDisposable? localScoreSubscription; private TaskCompletionSource? localFetchCompletionSource; @@ -45,10 +45,10 @@ namespace osu.Game.Online.Leaderboards public Task FetchWithCriteriaAsync(LeaderboardCriteria newCriteria) { - if (criteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false) + if (CurrentCriteria?.Equals(newCriteria) == true && lastFetchCompletionSource?.Task.IsFaulted == false) return lastFetchCompletionSource?.Task ?? Task.FromResult(Scores.Value); - criteria = newCriteria; + CurrentCriteria = newCriteria; localScoreSubscription?.Dispose(); inFlightOnlineRequest?.Cancel(); lastFetchCompletionSource?.TrySetCanceled(); @@ -110,7 +110,7 @@ namespace osu.Game.Online.Leaderboards private void localScoresChanged(IRealmCollection sender, ChangeSet? changes) { - Debug.Assert(criteria != null); + Debug.Assert(CurrentCriteria != null); // This subscription may fire from changes to linked beatmaps, which we don't care about. // It's currently not possible for a score to be modified after insertion, so we can safely ignore callbacks with only modifications. @@ -119,9 +119,9 @@ namespace osu.Game.Online.Leaderboards var newScores = sender.AsEnumerable(); - if (criteria.ExactMods != null) + if (CurrentCriteria.ExactMods != null) { - if (!criteria.ExactMods.Any()) + if (!CurrentCriteria.ExactMods.Any()) { // we need to filter out all scores that have any mods to get all local nomod scores newScores = newScores.Where(s => !s.Mods.Any()); @@ -130,7 +130,7 @@ namespace osu.Game.Online.Leaderboards { // otherwise find all the scores that have all of the currently selected mods (similar to how web applies mod filters) // we're creating and using a string HashSet representation of selected mods so that it can be translated into the DB query itself - var selectedMods = criteria.ExactMods.Select(m => m.Acronym).ToHashSet(); + var selectedMods = CurrentCriteria.ExactMods.Select(m => m.Acronym).ToHashSet(); newScores = newScores.Where(s => selectedMods.SetEquals(s.Mods.Select(m => m.Acronym))); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3381553970..76d370b4dc 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -47,6 +47,7 @@ using osu.Game.IO; using osu.Game.Localisation; using osu.Game.Online; using osu.Game.Online.Chat; +using osu.Game.Online.Leaderboards; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; @@ -67,6 +68,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; +using osu.Game.Screens.Select.Leaderboards; using osu.Game.Seasonal; using osu.Game.Skinning; using osu.Game.Updater; @@ -784,6 +786,24 @@ namespace osu.Game if (!Beatmap.Value.BeatmapInfo.Equals(databasedBeatmap)) Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); + var currentLeaderboard = LeaderboardManager.CurrentCriteria; + + bool leaderboardBeatmapMatches = currentLeaderboard != null && databasedBeatmap.Equals(currentLeaderboard.Beatmap); + bool leaderboardRulesetMatches = currentLeaderboard != null && databasedScore.ScoreInfo.Ruleset.Equals(currentLeaderboard.Ruleset); + + if (!leaderboardBeatmapMatches || !leaderboardRulesetMatches) + { + var newLeaderboard = currentLeaderboard != null + ? currentLeaderboard with { Beatmap = databasedBeatmap, Ruleset = databasedScore.ScoreInfo.Ruleset } + : new LeaderboardCriteria(databasedBeatmap, databasedScore.ScoreInfo.Ruleset, BeatmapLeaderboardScope.Global, null); + LeaderboardManager.FetchWithCriteriaAsync(newLeaderboard) + .ContinueWith(t => + { + if (t.Exception != null) + Logger.Log($@"Failed to fetch leaderboards when displaying results: {t.Exception}", LoggingTarget.Network); + }); + } + switch (presentType) { case ScorePresentType.Gameplay: diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fb28b8c5a4..9a8a127886 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -204,7 +204,7 @@ namespace osu.Game private UserLookupCache userCache; private BeatmapLookupCache beatmapCache; - private LeaderboardManager leaderboardManager; + protected LeaderboardManager LeaderboardManager { get; private set; } private RulesetConfigCache rulesetConfigCache; @@ -367,8 +367,8 @@ namespace osu.Game dependencies.CacheAs>(Beatmap); dependencies.CacheAs(Beatmap); - dependencies.Cache(leaderboardManager = new LeaderboardManager()); - base.Content.Add(leaderboardManager); + dependencies.Cache(LeaderboardManager = new LeaderboardManager()); + base.Content.Add(LeaderboardManager); // add api components to hierarchy. if (API is APIAccess apiAccess) From 34180c62eb7d4f46131d4f3763f108aebf17de6a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 10 Apr 2025 17:47:39 +0900 Subject: [PATCH 14/58] Add display to show completed playlist items --- .../TestSceneDrawableRoomPlaylist.cs | 13 +++ .../DrawableRoomPlaylistItemStrings.cs | 19 ++++ osu.Game/Online/Rooms/ItemAttemptsCount.cs | 12 +++ osu.Game/Online/Rooms/PlaylistItem.cs | 7 ++ .../OnlinePlay/DrawableRoomPlaylistItem.cs | 90 ++++++++++++++++++- .../Playlists/PlaylistsRoomSubScreen.cs | 25 ++++++ 6 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Localisation/DrawableRoomPlaylistItemStrings.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 18cd720bf2..7e19f45a00 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -105,6 +105,19 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("no item selected", () => playlist.SelectedItem.Value == null); } + [Test] + public void TestMarkCompleted() + { + createPlaylist(); + AddStep("mark some items as complete", () => + { + playlist.Items[0].MarkCompleted(); + playlist.Items[2].MarkCompleted(); + playlist.Items[3].MarkCompleted(); + playlist.Items[5].MarkCompleted(); + }); + } + [Test] public void TestSelectable() { diff --git a/osu.Game/Localisation/DrawableRoomPlaylistItemStrings.cs b/osu.Game/Localisation/DrawableRoomPlaylistItemStrings.cs new file mode 100644 index 0000000000..44616c03ca --- /dev/null +++ b/osu.Game/Localisation/DrawableRoomPlaylistItemStrings.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class DrawableRoomPlaylistItemStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.DrawableRoomPlaylistItem"; + + /// + /// "You have completed this beatmap" + /// + public static LocalisableString CompletedTooltip => new TranslatableString(getKey(@"completed_tooltip"), @"You have completed this beatmap"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Online/Rooms/ItemAttemptsCount.cs b/osu.Game/Online/Rooms/ItemAttemptsCount.cs index dc86897660..17b7f093f4 100644 --- a/osu.Game/Online/Rooms/ItemAttemptsCount.cs +++ b/osu.Game/Online/Rooms/ItemAttemptsCount.cs @@ -10,10 +10,22 @@ namespace osu.Game.Online.Rooms /// public class ItemAttemptsCount { + /// + /// The playlist item this object describes. + /// [JsonProperty("id")] public int PlaylistItemID { get; set; } + /// + /// The number of times the user attempted the playlist item. + /// [JsonProperty("attempts")] public int Attempts { get; set; } + + /// + /// Whether the user has a passing score on the playlist item. + /// + [JsonProperty("completed")] + public bool Completed { get; set; } } } diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index 427f31fc64..8ba62fd0e2 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -85,6 +85,11 @@ namespace osu.Game.Online.Rooms private readonly Bindable valid = new BindableBool(true); + [JsonIgnore] + public IBindable Completed => completed; + + private readonly Bindable completed = new BindableBool(false); + [JsonConstructor] private PlaylistItem() : this(new APIBeatmap()) @@ -118,6 +123,8 @@ namespace osu.Game.Online.Rooms public void MarkInvalid() => valid.Value = false; + public void MarkCompleted() => completed.Value = true; + #region Newtonsoft.Json implicit ShouldSerialize() methods // The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases. diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 9e585d584d..0afeaa9532 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -31,13 +31,14 @@ using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; -using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; +using osu.Game.Localisation; +using WebLocalisation = osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.OnlinePlay { @@ -76,6 +77,7 @@ namespace osu.Game.Screens.OnlinePlay private readonly DelayedLoadWrapper onScreenLoader; private readonly IBindable valid = new Bindable(); + private readonly IBindable completed = new Bindable(); private IBeatmapInfo? beatmap; private IRulesetInfo? ruleset; @@ -128,6 +130,7 @@ namespace osu.Game.Screens.OnlinePlay Item = item; valid.BindTo(item.Valid); + completed.BindTo(item.Completed); } [BackgroundDependencyLoader] @@ -525,9 +528,27 @@ namespace osu.Game.Screens.OnlinePlay private IEnumerable createButtons() => new[] { - beatmap == null ? Empty() : new PlaylistDownloadButton(beatmap), + new CompletionIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Visible = { BindTarget = completed } + }, + beatmap == null + ? Empty().With(d => + { + d.Anchor = Anchor.Centre; + d.Origin = Anchor.Centre; + }) + : new PlaylistDownloadButton(beatmap) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, showResultsButton = new GrayButton(FontAwesome.Solid.ChartPie) { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Size = new Vector2(30, 30), Action = () => RequestResults?.Invoke(Item), Alpha = AllowShowingResults ? 1 : 0, @@ -535,13 +556,17 @@ namespace osu.Game.Screens.OnlinePlay }, editButton = new PlaylistEditButton { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Size = new Vector2(30, 30), Alpha = AllowEditing ? 1 : 0, Action = () => RequestEdit?.Invoke(Item), - TooltipText = CommonStrings.ButtonsEdit + TooltipText = WebLocalisation.CommonStrings.ButtonsEdit }, removeButton = new PlaylistRemoveButton { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Size = new Vector2(30, 30), Alpha = AllowDeletion ? 1 : 0, Action = () => RequestDeletion?.Invoke(Item), @@ -768,5 +793,64 @@ namespace osu.Game.Screens.OnlinePlay this.allowInteraction = allowInteraction; } } + + private partial class CompletionIcon : CompositeDrawable, IHasTooltip + { + public readonly BindableBool Visible = new BindableBool(); + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(16), + Masking = true, + Colour = colours.Lime0, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.5f), + Colour = OsuColour.Gray(0.5f), + Icon = FontAwesome.Solid.Check + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Visible.BindValueChanged(onVisibleChanged, true); + } + + private void onVisibleChanged(ValueChangedEvent visible) + { + if (visible.NewValue) + { + Size = new Vector2(16); + Alpha = 1; + } + else + { + Size = Vector2.Zero; + Alpha = 0; + } + } + + public LocalisableString TooltipText => DrawableRoomPlaylistItemStrings.CompletedTooltip; + } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 053e3b97af..47219e42cb 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -465,6 +465,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }); updateSetupState(); + updateUserScore(); updateGameplayState(); } @@ -480,6 +481,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists case nameof(Room.RoomID): updateSetupState(); break; + + case nameof(Room.UserScore): + updateUserScore(); + break; } } @@ -507,12 +512,32 @@ namespace osu.Game.Screens.OnlinePlay.Playlists progressSection.Alpha = room.MaxAttempts != null ? 1 : 0; drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist); + updateUserScore(); + // Select an initial item for the user to help them get into a playable state quicker. SelectedItem.Value = room.Playlist.FirstOrDefault(); }); } } + /// + /// Responds to changes in to mark playlist items as completed. + /// + private void updateUserScore() + { + if (room.UserScore == null) + return; + + if (drawablePlaylist.Items.Count == 0) + return; + + foreach (var item in room.UserScore.PlaylistItemAttempts) + { + if (item.Completed) + drawablePlaylist.Items.Single(i => i.ID == item.PlaylistItemID).MarkCompleted(); + } + } + /// /// Adjusts the rate at which the is updated. /// From 3dc8a4c1ed26e5cb95d146bcccb333eb9e7d09ae Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 10 Apr 2025 18:44:44 +0900 Subject: [PATCH 15/58] Rename to `passed` --- osu.Game/Online/Rooms/ItemAttemptsCount.cs | 4 ++-- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Rooms/ItemAttemptsCount.cs b/osu.Game/Online/Rooms/ItemAttemptsCount.cs index 17b7f093f4..9ea2235500 100644 --- a/osu.Game/Online/Rooms/ItemAttemptsCount.cs +++ b/osu.Game/Online/Rooms/ItemAttemptsCount.cs @@ -25,7 +25,7 @@ namespace osu.Game.Online.Rooms /// /// Whether the user has a passing score on the playlist item. /// - [JsonProperty("completed")] - public bool Completed { get; set; } + [JsonProperty("passed")] + public bool Passed { get; set; } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 47219e42cb..9834598ac0 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -533,7 +533,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists foreach (var item in room.UserScore.PlaylistItemAttempts) { - if (item.Completed) + if (item.Passed) drawablePlaylist.Items.Single(i => i.ID == item.PlaylistItemID).MarkCompleted(); } } From 69035ef48f772c468f0e7dcc4e770dd306660a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Apr 2025 19:59:20 +0900 Subject: [PATCH 16/58] Fix status pill animating from zero height --- .../TestSceneBeatmapSetOnlineStatusPill.cs | 1 - .../Drawables/BeatmapSetOnlineStatusPill.cs | 28 ++++++++++--------- .../Cards/BeatmapCardExtraInfoRow.cs | 1 - .../BeatmapSet/BeatmapSetHeaderContent.cs | 1 - osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 - .../Select/Carousel/SetPanelContent.cs | 1 - osu.Game/Screens/SelectV2/PanelBeatmapSet.cs | 1 - .../SelectV2/PanelBeatmapStandalone.cs | 1 - 8 files changed, 15 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs index dcc4654437..2b95d7a554 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs @@ -28,7 +28,6 @@ namespace osu.Game.Tests.Visual.Beatmaps Spacing = new Vector2(0, 10), ChildrenEnumerable = Enum.GetValues(typeof(BeatmapOnlineStatus)).Cast().Select(status => new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Status = status diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 7b99ad40de..2a1dd536b8 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -19,10 +19,6 @@ namespace osu.Game.Beatmaps.Drawables { public partial class BeatmapSetOnlineStatusPill : CircularContainer, IHasTooltip { - private const double animation_duration = 400; - - private BeatmapOnlineStatus status; - public BeatmapOnlineStatus Status { get => status; @@ -34,30 +30,27 @@ namespace osu.Game.Beatmaps.Drawables status = value; if (IsLoaded) - { - AutoSizeDuration = (float)animation_duration; - AutoSizeEasing = Easing.OutQuint; - updateState(); - } } } + private BeatmapOnlineStatus status; + public float TextSize { - get => statusText.Font.Size; - set => statusText.Font = statusText.Font.With(size: value); + init => statusText.Font = statusText.Font.With(size: value); } public MarginPadding TextPadding { - get => statusText.Padding; - set => statusText.Padding = value; + init => statusText.Padding = value; } private readonly OsuSpriteText statusText; private readonly Box background; + private const double animation_duration = 400; + [Resolved] private OsuColour colours { get; set; } = null!; @@ -66,6 +59,7 @@ namespace osu.Game.Beatmaps.Drawables public BeatmapSetOnlineStatusPill() { + AutoSizeAxes = Axes.Both; Masking = true; Alpha = 0; @@ -105,6 +99,14 @@ namespace osu.Game.Beatmaps.Drawables return; } + // Only animate resizing if we already have a size. + // This avoids animating height from zero. + if (Width > 0) + { + AutoSizeDuration = (float)animation_duration; + AutoSizeEasing = Easing.OutQuint; + } + this.FadeIn(animation_duration, Easing.OutQuint); Color4 statusTextColour; diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs index 41513ec7a2..ee2f682708 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs @@ -30,7 +30,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Status = beatmapSet.Status, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index c72c2a6698..9b10f6156d 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -177,7 +177,6 @@ namespace osu.Game.Overlays.BeatmapSet { onlineStatusPill = new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, TextSize = 14, diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index fd1c944689..5a09780943 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -263,7 +263,6 @@ namespace osu.Game.Screens.Select }, new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Shear = -wedged_container_shear, diff --git a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs index 8d6fbbf256..c3ded16bd2 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs @@ -77,7 +77,6 @@ namespace osu.Game.Screens.Select.Carousel }, new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, TextSize = 11, diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs b/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs index c599c3e534..9e9ef612ea 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs @@ -97,7 +97,6 @@ namespace osu.Game.Screens.SelectV2 }, statusPill = new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, TextSize = 11, diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs index 948311a86e..f893bb0caf 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs @@ -117,7 +117,6 @@ namespace osu.Game.Screens.SelectV2 }, statusPill = new BeatmapSetOnlineStatusPill { - AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, TextSize = 11, From 57033fc1801b7243a97687cebc98caa41d96b6b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Apr 2025 20:10:30 +0900 Subject: [PATCH 17/58] Allow displaying "unknown" status in status pill --- .../TestSceneBeatmapSetOnlineStatusPill.cs | 30 +++++++++++++------ osu.Game/Beatmaps/BeatmapOnlineStatus.cs | 2 +- .../Drawables/BeatmapSetOnlineStatusPill.cs | 7 ++++- osu.Game/Graphics/OsuColour.cs | 3 ++ 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs index 2b95d7a554..82e02a9b6f 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Beatmaps { public partial class TestSceneBeatmapSetOnlineStatusPill : ThemeComparisonTestScene { + private bool showUnknownStatus; + protected override Drawable CreateContent() => new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -26,11 +28,20 @@ namespace osu.Game.Tests.Visual.Beatmaps Origin = Anchor.Centre, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10), - ChildrenEnumerable = Enum.GetValues(typeof(BeatmapOnlineStatus)).Cast().Select(status => new BeatmapSetOnlineStatusPill + ChildrenEnumerable = Enum.GetValues(typeof(BeatmapOnlineStatus)).Cast().Select(status => new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Status = status + RelativeSizeAxes = Axes.X, + Height = 20, + Children = new Drawable[] + { + new BeatmapSetOnlineStatusPill + { + ShowUnknownStatus = showUnknownStatus, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Status = status + } + } }) }; @@ -47,6 +58,12 @@ namespace osu.Game.Tests.Visual.Beatmaps pill.Width = 90; })); + AddStep("toggle show unknown", () => + { + showUnknownStatus = !showUnknownStatus; + CreateThemedContent(OverlayColourScheme.Red); + }); + AddStep("unset fixed width", () => statusPills.ForEach(pill => pill.AutoSizeAxes = Axes.Both)); } @@ -64,11 +81,6 @@ namespace osu.Game.Tests.Visual.Beatmaps pill.Status = BeatmapOnlineStatus.LocallyModified; break; - // skip none - case BeatmapOnlineStatus.LocallyModified: - pill.Status = BeatmapOnlineStatus.Graveyard; - break; - default: pill.Status = (pill.Status + 1); break; diff --git a/osu.Game/Beatmaps/BeatmapOnlineStatus.cs b/osu.Game/Beatmaps/BeatmapOnlineStatus.cs index 41393a8a39..d489aeda3f 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineStatus.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineStatus.cs @@ -14,10 +14,10 @@ namespace osu.Game.Beatmaps /// This is a special status given when local changes are made via the editor. /// Once in this state, online status changes should be ignored unless the beatmap is reverted or submitted. /// - [Description("Local")] [LocalisableDescription(typeof(SongSelectStrings), nameof(SongSelectStrings.LocallyModified))] LocallyModified = -4, + [Description("Unknown")] None = -3, [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatusGraveyard))] diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 2a1dd536b8..83b385bb8e 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -19,6 +19,11 @@ namespace osu.Game.Beatmaps.Drawables { public partial class BeatmapSetOnlineStatusPill : CircularContainer, IHasTooltip { + /// + /// Whether to show as "unknownn" instead of fading out. + /// + public bool ShowUnknownStatus { get; init; } + public BeatmapOnlineStatus Status { get => status; @@ -93,7 +98,7 @@ namespace osu.Game.Beatmaps.Drawables private void updateState() { - if (Status == BeatmapOnlineStatus.None) + if (Status == BeatmapOnlineStatus.None && !ShowUnknownStatus) { Hide(); return; diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 2c43876fb2..bc3047e624 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -120,6 +120,9 @@ namespace osu.Game.Graphics { switch (status) { + case BeatmapOnlineStatus.None: + return Color4.RosyBrown; + case BeatmapOnlineStatus.LocallyModified: return Color4.OrangeRed; From f9112066d3638477b088e0502665bbb97ede9a3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Apr 2025 17:42:17 +0900 Subject: [PATCH 18/58] Fix carousel handling of bleed areas The idea of specifying "bleed" is to make the carousel aware of its vertical display area. The top bleed is under the filter control; bottom beneath the toolbar. At the end of the day, the point of panel X offset incursion, and the scroll target for current selection, should be at the centre of the screen. The fixes match code which already exists in the previous implementation. Basically, without incorporating `BleedTop` into calculations a second time, the centre position would not match expectations (of being the centre including bleed). --- .../SongSelect/BeatmapCarouselV2TestScene.cs | 2 ++ osu.Game/Screens/SelectV2/Carousel.cs | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs b/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs index 2c902a466f..ad8004304a 100644 --- a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs +++ b/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs @@ -96,6 +96,8 @@ namespace osu.Game.Tests.Visual.SongSelect { Carousel = new BeatmapCarousel { + BleedTop = 200, + BleedBottom = 200, Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 800, diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs index 5339b5358b..21310b76a1 100644 --- a/osu.Game/Screens/SelectV2/Carousel.cs +++ b/osu.Game/Screens/SelectV2/Carousel.cs @@ -505,7 +505,7 @@ namespace osu.Game.Screens.SelectV2 private void scrollToSelection() { if (currentKeyboardSelection.CarouselItem != null) - Scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight); + Scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight + BleedTop); } #endregion @@ -519,17 +519,17 @@ namespace osu.Game.Screens.SelectV2 /// /// The position of the lower visible bound with respect to the current scroll position. /// - private float visibleBottomBound => (float)(Scroll.Current + DrawHeight + BleedBottom); + private float visibleBottomBound; /// /// The position of the upper visible bound with respect to the current scroll position. /// - private float visibleUpperBound => (float)(Scroll.Current - BleedTop); + private float visibleUpperBound; /// /// Half the height of the visible content. /// - private float visibleHalfHeight => (DrawHeight + BleedBottom + BleedTop) / 2; + private float visibleHalfHeight; protected override void Update() { @@ -538,6 +538,10 @@ namespace osu.Game.Screens.SelectV2 if (carouselItems == null) return; + visibleBottomBound = (float)(Scroll.Current + DrawHeight + BleedBottom); + visibleUpperBound = (float)(Scroll.Current - BleedTop); + visibleHalfHeight = (DrawHeight + BleedBottom + BleedTop) / 2; + if (!selectionValid.IsValid) { refreshAfterSelection(); @@ -582,7 +586,7 @@ namespace osu.Game.Screens.SelectV2 protected virtual float GetPanelXOffset(Drawable panel) { Vector2 posInScroll = Scroll.ToLocalSpace(panel.ScreenSpaceDrawQuad.Centre); - float dist = Math.Abs(1f - posInScroll.Y / visibleHalfHeight); + float dist = Math.Abs(1f - (posInScroll.Y + BleedTop) / visibleHalfHeight); return offsetX(dist, visibleHalfHeight); } From 430b22b383686441bf6dfae8a3419f620c93c0dd Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 20 Mar 2025 11:41:05 -0400 Subject: [PATCH 19/58] Remove previous beatmap wedge implementation --- .../SongSelectV2/TestSceneBeatmapInfoWedge.cs | 213 ----------- .../TestSceneDifficultyNameContent.cs | 44 --- .../Screens/SelectV2/BeatmapInfoWedgeV2.cs | 330 ------------------ .../SelectV2/Wedge/DifficultyNameContent.cs | 88 ----- .../Wedge/LocalDifficultyNameContent.cs | 34 -- 5 files changed, 709 deletions(-) delete mode 100644 osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapInfoWedge.cs delete mode 100644 osu.Game.Tests/Visual/SongSelectV2/TestSceneDifficultyNameContent.cs delete mode 100644 osu.Game/Screens/SelectV2/BeatmapInfoWedgeV2.cs delete mode 100644 osu.Game/Screens/SelectV2/Wedge/DifficultyNameContent.cs delete mode 100644 osu.Game/Screens/SelectV2/Wedge/LocalDifficultyNameContent.cs diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapInfoWedge.cs deleted file mode 100644 index 5b717887e2..0000000000 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapInfoWedge.cs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Legacy; -using osu.Game.Screens.Select; -using osu.Game.Screens.SelectV2; - -namespace osu.Game.Tests.Visual.SongSelectV2 -{ - public partial class TestSceneBeatmapInfoWedge : SongSelectComponentsTestScene - { - private RulesetStore rulesets = null!; - private TestBeatmapInfoWedgeV2 infoWedge = null!; - private readonly List beatmaps = new List(); - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - public override void SetUpSteps() - { - base.SetUpSteps(); - - AddStep("reset mods", () => SelectedMods.SetDefault()); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - AddRange(new Drawable[] - { - // This exists only to make the wedge more visible in the test scene - new Box - { - Y = -20, - Colour = Colour4.Cornsilk.Darken(0.2f), - Height = BeatmapInfoWedgeV2.WEDGE_HEIGHT + 40, - Width = 0.65f, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = 20, Left = -10 } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 20 }, - Child = infoWedge = new TestBeatmapInfoWedgeV2 - { - Width = 0.6f, - RelativeSizeAxes = Axes.X, - }, - } - }); - - AddSliderStep("change star difficulty", 0, 11.9, 5.55, v => - { - foreach (var hasCurrentValue in infoWedge.ChildrenOfType>()) - hasCurrentValue.Current.Value = new StarDifficulty(v, 0); - }); - } - - [Test] - public void TestRulesetChange() - { - selectBeatmap(Beatmap.Value.Beatmap); - - AddWaitStep("wait for select", 3); - - foreach (var rulesetInfo in rulesets.AvailableRulesets) - { - var instance = rulesetInfo.CreateInstance(); - var testBeatmap = createTestBeatmap(rulesetInfo); - - beatmaps.Add(testBeatmap); - - setRuleset(rulesetInfo); - - selectBeatmap(testBeatmap); - - testBeatmapLabels(instance); - } - } - - [Test] - public void TestWedgeVisibility() - { - AddStep("hide", () => { infoWedge.Hide(); }); - AddWaitStep("wait for hide", 3); - AddAssert("check visibility", () => infoWedge.Alpha == 0); - AddStep("show", () => { infoWedge.Show(); }); - AddWaitStep("wait for show", 1); - AddAssert("check visibility", () => infoWedge.Alpha > 0); - } - - private void testBeatmapLabels(Ruleset ruleset) - { - AddAssert("check title", () => infoWedge.Info!.TitleLabel.Current.Value == $"{ruleset.ShortName}Title"); - AddAssert("check artist", () => infoWedge.Info!.ArtistLabel.Current.Value == $"{ruleset.ShortName}Artist"); - } - - [Test] - public void TestTruncation() - { - selectBeatmap(createLongMetadata()); - } - - [Test] - public void TestNullBeatmapWithBackground() - { - selectBeatmap(null); - AddAssert("check default title", () => infoWedge.Info!.TitleLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Title); - AddAssert("check default artist", () => infoWedge.Info!.ArtistLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Artist); - AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); - } - - private void setRuleset(RulesetInfo rulesetInfo) - { - Container? containerBefore = null; - - AddStep("set ruleset", () => - { - // wedge content is only refreshed if the ruleset changes, so only wait for load in that case. - if (!rulesetInfo.Equals(Ruleset.Value)) - containerBefore = infoWedge.DisplayedContent; - - Ruleset.Value = rulesetInfo; - }); - - AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore); - } - - private void selectBeatmap(IBeatmap? b) - { - Container? containerBefore = null; - - AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () => - { - containerBefore = infoWedge.DisplayedContent; - infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b); - infoWedge.Show(); - }); - - AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore); - } - - private IBeatmap createTestBeatmap(RulesetInfo ruleset) - { - List objects = new List(); - for (double i = 0; i < 50000; i += 1000) - objects.Add(new TestHitObject { StartTime = i }); - - return new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Author = { Username = $"{ruleset.ShortName}Author" }, - Artist = $"{ruleset.ShortName}Artist", - Source = $"{ruleset.ShortName}Source", - Title = $"{ruleset.ShortName}Title" - }, - Ruleset = ruleset, - StarRating = 6, - DifficultyName = $"{ruleset.ShortName}Version", - Difficulty = new BeatmapDifficulty() - }, - HitObjects = objects - }; - } - - private IBeatmap createLongMetadata() - { - return new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Author = { Username = "WWWWWWWWWWWWWWW" }, - Artist = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Artist", - Source = "Verrrrry long Source", - Title = "Verrrrry long Title" - }, - DifficultyName = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version", - Status = BeatmapOnlineStatus.Graveyard, - }, - }; - } - - private partial class TestBeatmapInfoWedgeV2 : BeatmapInfoWedgeV2 - { - public new Container? DisplayedContent => base.DisplayedContent; - public new WedgeInfoText? Info => base.Info; - } - - private class TestHitObject : ConvertHitObject; - } -} diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneDifficultyNameContent.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneDifficultyNameContent.cs deleted file mode 100644 index 49e7e2bc1a..0000000000 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneDifficultyNameContent.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using NUnit.Framework; -using osu.Framework.Localisation; -using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Screens.SelectV2.Wedge; - -namespace osu.Game.Tests.Visual.SongSelectV2 -{ - public partial class TestSceneDifficultyNameContent : SongSelectComponentsTestScene - { - private DifficultyNameContent? difficultyNameContent; - - [Test] - public void TestLocalBeatmap() - { - AddStep("set component", () => Child = difficultyNameContent = new LocalDifficultyNameContent()); - - AddAssert("difficulty name is not set", () => LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType().Single().Text)); - AddAssert("author is not set", () => LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType().Single().ChildrenOfType().Single().Text)); - - AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - DifficultyName = "really long difficulty name that gets truncated", - Metadata = new BeatmapMetadata - { - Author = { Username = "really long username that is autosized" }, - }, - OnlineID = 1, - } - })); - - AddAssert("difficulty name is set", () => !LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType().Single().Text)); - AddAssert("author is set", () => !LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType().Single().ChildrenOfType().Single().Text)); - } - } -} diff --git a/osu.Game/Screens/SelectV2/BeatmapInfoWedgeV2.cs b/osu.Game/Screens/SelectV2/BeatmapInfoWedgeV2.cs deleted file mode 100644 index b294896c77..0000000000 --- a/osu.Game/Screens/SelectV2/BeatmapInfoWedgeV2.cs +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Threading; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; -using osu.Game.Screens.Select; -using osuTK; - -namespace osu.Game.Screens.SelectV2 -{ - public partial class BeatmapInfoWedgeV2 : VisibilityContainer - { - public const float WEDGE_HEIGHT = 120; - private const float shear_width = 21; - private const float transition_duration = 250; - private const float corner_radius = 10; - private const float colour_bar_width = 30; - - /// Todo: move this const out to song select when more new design elements are implemented for the beatmap details area, since it applies to text alignment of various elements - private const float text_margin = 62; - - private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / WEDGE_HEIGHT, 0); - - [Resolved] - private IBindable ruleset { get; set; } = null!; - - [Resolved] - private OsuColour colours { get; set; } = null!; - - [Resolved] - private BeatmapDifficultyCache difficultyCache { get; set; } = null!; - - protected Container? DisplayedContent { get; private set; } - - protected WedgeInfoText? Info { get; private set; } - - private Container difficultyColourBar = null!; - private StarCounter starCounter = null!; - private StarRatingDisplay starRatingDisplay = null!; - private BeatmapSetOnlineStatusPill statusPill = null!; - private Container content = null!; - - private IBindable? starDifficulty; - private CancellationTokenSource? cancellationSource; - - public BeatmapInfoWedgeV2() - { - Height = WEDGE_HEIGHT; - Shear = wedged_container_shear; - Masking = true; - Margin = new MarginPadding { Left = -corner_radius }; - EdgeEffect = new EdgeEffectParameters - { - Colour = Colour4.Black.Opacity(0.2f), - Type = EdgeEffectType.Shadow, - Radius = 3, - }; - CornerRadius = corner_radius; - } - - [BackgroundDependencyLoader] - private void load() - { - Child = content = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - // These elements can't be grouped with the rest of the content, due to being present either outside or under the backgrounds area - difficultyColourBar = new Container - { - Colour = Colour4.Transparent, - Depth = float.MaxValue, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - - // By limiting the width we avoid this box showing up as an outline around the drawables that are on top of it. - Width = colour_bar_width + corner_radius, - Child = new Box { RelativeSizeAxes = Axes.Both } - }, - new Container - { - // Applying the shear to this container and nesting the starCounter inside avoids - // the deformation that occurs if the shear is applied to the starCounter whilst rotated - Shear = -wedged_container_shear, - X = -colour_bar_width / 2, - Anchor = Anchor.CentreRight, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Width = colour_bar_width, - Child = starCounter = new StarCounter - { - Rotation = (float)(Math.Atan(shear_width / WEDGE_HEIGHT) * (180 / Math.PI)), - Colour = Colour4.Transparent, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(0.35f), - Direction = FillDirection.Vertical - } - }, - new FillFlowContainer - { - Name = "Topright-aligned metadata", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Top = 3, Right = colour_bar_width + 8 }, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(0, 5), - Depth = float.MinValue, - Children = new Drawable[] - { - starRatingDisplay = new StarRatingDisplay(default, animated: true) - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - Alpha = 0, - }, - statusPill = new BeatmapSetOnlineStatusPill - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Shear = -wedged_container_shear, - TextSize = 11, - TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, - Alpha = 0, - } - } - }, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - ruleset.BindValueChanged(_ => updateDisplay()); - - starRatingDisplay.Current.BindValueChanged(s => - { - // use actual stars as star counter has its own animation - starCounter.Current = (float)s.NewValue.Stars; - }, true); - - starRatingDisplay.DisplayedStars.BindValueChanged(s => - { - // sync color with star rating display - starCounter.Colour = s.NewValue >= 6.5 ? colours.Orange1 : Colour4.Black.Opacity(0.75f); - difficultyColourBar.FadeColour(colours.ForStarDifficulty(s.NewValue)); - }, true); - } - - private const double animation_duration = 600; - - protected override void PopIn() - { - this.MoveToX(0, animation_duration, Easing.OutQuint); - this.FadeIn(200, Easing.In); - } - - protected override void PopOut() - { - this.MoveToX(-150, animation_duration, Easing.OutQuint); - this.FadeOut(200, Easing.OutQuint); - } - - private WorkingBeatmap beatmap = null!; - - public WorkingBeatmap Beatmap - { - get => beatmap; - set - { - if (beatmap == value) return; - - beatmap = value; - - updateDisplay(); - } - } - - private Container? loadingInfo; - - private void updateDisplay() - { - statusPill.Status = beatmap.BeatmapInfo.Status; - - starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); - - starDifficulty.BindValueChanged(s => - { - starRatingDisplay.Current.Value = s.NewValue ?? default; - - starRatingDisplay.FadeIn(transition_duration); - }); - - Scheduler.AddOnce(() => - { - LoadComponentAsync(loadingInfo = new Container - { - Padding = new MarginPadding { Right = colour_bar_width }, - RelativeSizeAxes = Axes.Both, - Depth = DisplayedContent?.Depth + 1 ?? 0, - Child = new Container - { - Masking = true, - CornerRadius = corner_radius, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - // TODO: New wedge design uses a coloured horizontal gradient for its background, however this lacks implementation information in the figma draft. - // pending https://www.figma.com/file/DXKwqZhD5yyb1igc3mKo1P?node-id=2980:3361#340801912 being answered. - new BeatmapInfoWedgeBackground(beatmap) { Shear = -Shear }, - Info = new WedgeInfoText(beatmap) { Shear = -Shear } - } - } - }, d => - { - // Ensure we are the most recent loaded wedge. - if (d != loadingInfo) return; - - removeOldInfo(); - content.Add(DisplayedContent = d); - }); - }); - - void removeOldInfo() - { - DisplayedContent?.FadeOut(transition_duration); - DisplayedContent?.Expire(); - DisplayedContent = null; - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - cancellationSource?.Cancel(); - } - - public partial class WedgeInfoText : Container - { - public OsuSpriteText TitleLabel { get; private set; } = null!; - public OsuSpriteText ArtistLabel { get; private set; } = null!; - - private readonly WorkingBeatmap working; - - public WedgeInfoText(WorkingBeatmap working) - { - this.working = working; - - RelativeSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load(SongSelect? songSelect, LocalisationManager localisation) - { - var metadata = working.Metadata; - - var titleText = new RomanisableString(metadata.TitleUnicode, metadata.Title); - var artistText = new RomanisableString(metadata.ArtistUnicode, metadata.Artist); - - Child = new FillFlowContainer - { - Name = "Top-left aligned metadata", - Direction = FillDirection.Vertical, - Padding = new MarginPadding { Left = text_margin, Top = 12 }, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Children = new Drawable[] - { - new OsuHoverContainer - { - AutoSizeAxes = Axes.Both, - Action = () => songSelect?.Search(titleText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript)), - Child = TitleLabel = new TruncatingSpriteText - { - Shadow = true, - Text = titleText, - Font = OsuFont.TorusAlternate.With(size: 40, weight: FontWeight.SemiBold), - }, - }, - new OsuHoverContainer - { - AutoSizeAxes = Axes.Both, - Action = () => songSelect?.Search(artistText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript)), - Child = ArtistLabel = new TruncatingSpriteText - { - // TODO : figma design has a diffused shadow, instead of the solid one present here, not possible currently as far as i'm aware. - Shadow = true, - Text = artistText, - // Not sure if this should be semi bold or medium - Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), - }, - }, - } - }; - } - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - // best effort to confine the auto-sized text to wedge bounds - // the artist label doesn't have an extra text_margin as it doesn't touch the right metadata - TitleLabel.MaxWidth = DrawWidth - text_margin * 2 - shear_width; - ArtistLabel.MaxWidth = DrawWidth - text_margin - shear_width; - } - } - } -} diff --git a/osu.Game/Screens/SelectV2/Wedge/DifficultyNameContent.cs b/osu.Game/Screens/SelectV2/Wedge/DifficultyNameContent.cs deleted file mode 100644 index 4a3dc34cf9..0000000000 --- a/osu.Game/Screens/SelectV2/Wedge/DifficultyNameContent.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Localisation; -using osu.Game.Overlays; - -namespace osu.Game.Screens.SelectV2.Wedge -{ - public abstract partial class DifficultyNameContent : CompositeDrawable - { - protected OsuSpriteText DifficultyName = null!; - private OsuSpriteText mappedByLabel = null!; - protected OsuHoverContainer MapperLink = null!; - protected OsuSpriteText MapperName = null!; - - protected DifficultyNameContent() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - DifficultyName = new TruncatingSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold), - }, - mappedByLabel = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - // TODO: better null display? beatmap carousel panels also just show this text currently. - Text = " mapped by ", - Font = OsuFont.GetFont(size: 14), - }, - // This is not a `LinkFlowContainer` as there are single-frame layout issues when Update() - // is being used for layout, see https://github.com/ppy/osu-framework/issues/3369. - MapperLink = new MapperLinkContainer - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, - Child = MapperName = new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 14), - } - }, - } - }; - } - - protected override void Update() - { - base.Update(); - - // truncate difficulty name when width exceeds bounds, prioritizing mapper name display - DifficultyName.MaxWidth = Math.Max(DrawWidth - mappedByLabel.DrawWidth - - MapperName.DrawWidth, 0); - } - - private partial class MapperLinkContainer : OsuHoverContainer - { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider? overlayColourProvider, OsuColour colours) - { - TooltipText = ContextMenuStrings.ViewProfile; - IdleColour = overlayColourProvider?.Light2 ?? colours.Blue; - } - } - } -} diff --git a/osu.Game/Screens/SelectV2/Wedge/LocalDifficultyNameContent.cs b/osu.Game/Screens/SelectV2/Wedge/LocalDifficultyNameContent.cs deleted file mode 100644 index 66f8cb02b2..0000000000 --- a/osu.Game/Screens/SelectV2/Wedge/LocalDifficultyNameContent.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) ppy Pty Ltd . 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.Game.Beatmaps; -using osu.Game.Online; -using osu.Game.Online.Chat; - -namespace osu.Game.Screens.SelectV2.Wedge -{ - public partial class LocalDifficultyNameContent : DifficultyNameContent - { - [Resolved] - private IBindable beatmap { get; set; } = null!; - - [Resolved] - private ILinkHandler? linkHandler { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - beatmap.BindValueChanged(b => - { - DifficultyName.Text = b.NewValue.BeatmapInfo.DifficultyName; - - // TODO: should be the mapper of the guest difficulty, but that isn't stored correctly yet (see https://github.com/ppy/osu/issues/12965) - MapperName.Text = b.NewValue.Metadata.Author.Username; - MapperLink.Action = () => linkHandler?.HandleLink(new LinkDetails(LinkAction.OpenUserProfile, b.NewValue.Metadata.Author)); - }, true); - } - } -} From ba5932c1dd4eaff2d6e61a9b4dabc3e52dc5e36b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Apr 2025 02:11:45 +0900 Subject: [PATCH 20/58] Revert "Use median for statistic display" This reverts commit fa06643bb6c0aacde659640ae0a65c68ab9b0c61. Revert "Remove mean hit error calculation" This reverts commit b3c578e5455c572e34e2def301ba657182747149. --- osu.Game/Rulesets/Scoring/HitEventExtensions.cs | 17 +++++++++++++++++ .../Ranking/Statistics/AverageHitError.cs | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs index 39fc8b357b..01d800a351 100644 --- a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs +++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs @@ -54,6 +54,23 @@ namespace osu.Game.Rulesets.Scoring return result; } + /// + /// Calculates the average hit offset/error for a sequence of s, where negative numbers mean the user hit too early on average. + /// + /// + /// A non-null value if unstable rate could be calculated, + /// and if unstable rate cannot be calculated due to being empty. + /// + public static double? CalculateAverageHitError(this IEnumerable hitEvents) + { + double[] timeOffsets = hitEvents.Where(AffectsUnstableRate).Select(ev => ev.TimeOffset).ToArray(); + + if (timeOffsets.Length == 0) + return null; + + return timeOffsets.Average(); + } + /// /// Calculates the median hit offset/error for a sequence of s, where negative numbers mean the user hit too early on average. /// diff --git a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs index 29df085c62..fb7107cc88 100644 --- a/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs +++ b/osu.Game/Screens/Ranking/Statistics/AverageHitError.cs @@ -8,18 +8,18 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Screens.Ranking.Statistics { /// - /// Displays the average hit error statistic for a given play. + /// Displays the unstable rate statistic for a given play. /// public partial class AverageHitError : SimpleStatisticItem { /// /// Creates and computes an statistic. /// - /// Sequence of s to calculate the average hit error based on. + /// Sequence of s to calculate the unstable rate based on. public AverageHitError(IEnumerable hitEvents) : base("Average Hit Error") { - Value = hitEvents.CalculateMedianHitError(); + Value = hitEvents.CalculateAverageHitError(); } protected override string DisplayValue(double? value) => value == null ? "(not available)" : $"{Math.Abs(value.Value):N2} ms {(value.Value < 0 ? "early" : "late")}"; From 6bb84e4364256df75949a56cc4d67023a773f00c Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 20 Mar 2025 11:46:42 -0400 Subject: [PATCH 21/58] Update API beatmap model to include user play count --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 055d2dd8e2..20494a1cbf 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -32,6 +32,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"playcount")] public int PlayCount { get; set; } + [JsonProperty(@"current_user_playcount")] + public int UserPlayCount { get; set; } + [JsonProperty(@"passcount")] public int PassCount { get; set; } From 55dc64e5b6bafcc8b45f4214f432591f5ef888a0 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 20 Mar 2025 11:51:23 -0400 Subject: [PATCH 22/58] Add `DarkOrange` colour set --- osu.Game/Graphics/OsuColour.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 2c43876fb2..260ff9f797 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -23,6 +23,7 @@ namespace osu.Game.Graphics /// /// Retrieves the colour for a given point in the star range. /// + // todo: fix stupid array public Color4 ForStarDifficulty(double starDifficulty) => ColourUtils.SampleFromLinearGradient(new[] { (0.1f, Color4Extensions.FromHex("aaaaaa")), @@ -403,6 +404,12 @@ namespace osu.Game.Graphics public readonly Color4 Orange3 = Color4Extensions.FromHex(@"cca633"); public readonly Color4 Orange4 = Color4Extensions.FromHex(@"6b5c2e"); + public readonly Color4 DarkOrange0 = Color4Extensions.FromHex(@"ffbb99"); + public readonly Color4 DarkOrange1 = Color4Extensions.FromHex(@"ff9966"); + public readonly Color4 DarkOrange2 = Color4Extensions.FromHex(@"eb7e47"); + public readonly Color4 DarkOrange3 = Color4Extensions.FromHex(@"cc6633"); + public readonly Color4 DarkOrange4 = Color4Extensions.FromHex(@"6b422e"); + public readonly Color4 Red0 = Color4Extensions.FromHex(@"ff9b9b"); public readonly Color4 Red1 = Color4Extensions.FromHex(@"ff6666"); public readonly Color4 Red2 = Color4Extensions.FromHex(@"eb4747"); From 36a11d4bf7afa3ca238a561aa2e67ed498aed440 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 20 Mar 2025 11:52:13 -0400 Subject: [PATCH 23/58] Add specifications for new song select icons --- osu.Game/Graphics/OsuIcon.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index 9879ef5d14..84ff86a5e5 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -115,6 +115,7 @@ namespace osu.Game.Graphics public static IconUsage ChangelogB => get(OsuIconMapping.ChangelogB); public static IconUsage Chat => get(OsuIconMapping.Chat); public static IconUsage CheckCircle => get(OsuIconMapping.CheckCircle); + public static IconUsage Clock => get(OsuIconMapping.Clock); public static IconUsage CollapseA => get(OsuIconMapping.CollapseA); public static IconUsage Collections => get(OsuIconMapping.Collections); public static IconUsage Cross => get(OsuIconMapping.Cross); @@ -141,6 +142,7 @@ namespace osu.Game.Graphics public static IconUsage Input => get(OsuIconMapping.Input); public static IconUsage Maintenance => get(OsuIconMapping.Maintenance); public static IconUsage Megaphone => get(OsuIconMapping.Megaphone); + public static IconUsage Metronome => get(OsuIconMapping.Metronome); public static IconUsage Music => get(OsuIconMapping.Music); public static IconUsage News => get(OsuIconMapping.News); public static IconUsage Next => get(OsuIconMapping.Next); @@ -204,6 +206,9 @@ namespace osu.Game.Graphics [Description(@"check-circle")] CheckCircle, + [Description(@"clock")] + Clock, + [Description(@"collapse-a")] CollapseA, @@ -282,6 +287,9 @@ namespace osu.Game.Graphics [Description(@"megaphone")] Megaphone, + [Description(@"metronome")] + Metronome, + [Description(@"music")] Music, From 1dbcbbde1538876bbd04e93e66348c1e547b44e4 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 23 Mar 2025 23:02:38 -0400 Subject: [PATCH 24/58] Shorten beatmap hit statistics names --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs | 6 +++--- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs | 4 ++-- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 7 +++---- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs index 1f05d66b86..01cec1d815 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -21,19 +21,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { new BeatmapStatistic { - Name = @"Fruit Count", + Name = @"Fruits", Content = fruits.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), }, new BeatmapStatistic { - Name = @"Juice Stream Count", + Name = @"Juice Streams", Content = juiceStreams.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), }, new BeatmapStatistic { - Name = @"Banana Shower Count", + Name = @"Banana Showers", Content = bananaShowers.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 8222e5477d..8ddcfa128a 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { new BeatmapStatistic { - Name = @"Note Count", + Name = @"Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = notes.ToString(), }, new BeatmapStatistic { - Name = @"Hold Note Count", + Name = @"Hold Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = holdNotes.ToString(), }, diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index a5282877ee..730a194751 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Beatmaps @@ -21,19 +20,19 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { new BeatmapStatistic { - Name = BeatmapsetsStrings.ShowStatsCountCircles, + Name = "Circles", Content = circles.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), }, new BeatmapStatistic { - Name = BeatmapsetsStrings.ShowStatsCountSliders, + Name = "Sliders", Content = sliders.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), }, new BeatmapStatistic { - Name = @"Spinner Count", + Name = @"Spinners", Content = spinners.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs index 41fe63a553..0781485ab8 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs @@ -20,19 +20,19 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { new BeatmapStatistic { - Name = @"Hit Count", + Name = @"Hits", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = hits.ToString(), }, new BeatmapStatistic { - Name = @"Drumroll Count", + Name = @"Drumrolls", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = drumRolls.ToString(), }, new BeatmapStatistic { - Name = @"Swell Count", + Name = @"Swells", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), Content = swells.ToString(), } From 5c54e57d6d8aa803d3794be3581c81c73af77a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 11 Apr 2025 08:01:34 +0200 Subject: [PATCH 25/58] Remove redundant initialisers --- osu.Game/Screens/SelectV2/Carousel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs index 21310b76a1..7b1fd6e999 100644 --- a/osu.Game/Screens/SelectV2/Carousel.cs +++ b/osu.Game/Screens/SelectV2/Carousel.cs @@ -38,12 +38,12 @@ namespace osu.Game.Screens.SelectV2 /// /// Height of the area above the carousel that should be treated as visible due to transparency of elements in front of it. /// - public float BleedTop { get; set; } = 0; + public float BleedTop { get; set; } /// /// Height of the area below the carousel that should be treated as visible due to transparency of elements in front of it. /// - public float BleedBottom { get; set; } = 0; + public float BleedBottom { get; set; } /// /// The number of pixels outside the carousel's vertical bounds to manifest drawables. From 9911e0819eabe986eabe310cfac97ff6330e36c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 11 Apr 2025 10:13:49 +0200 Subject: [PATCH 26/58] Reduce bleed in tests to allow them to pass --- .../Visual/SongSelect/BeatmapCarouselV2TestScene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs b/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs index ad8004304a..f2faeab1c4 100644 --- a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs +++ b/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs @@ -96,8 +96,8 @@ namespace osu.Game.Tests.Visual.SongSelect { Carousel = new BeatmapCarousel { - BleedTop = 200, - BleedBottom = 200, + BleedTop = 50, + BleedBottom = 50, Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 800, From c52dc9ffe86086e9728e06dd7a0ab0eec816f32c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Apr 2025 02:33:27 +0900 Subject: [PATCH 27/58] Update difficulty spectrum retrieval function --- osu.Game/Graphics/OsuColour.cs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 260ff9f797..dec16d65bd 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -20,13 +20,9 @@ namespace osu.Game.Graphics public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f); public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255); - /// - /// Retrieves the colour for a given point in the star range. - /// - // todo: fix stupid array - public Color4 ForStarDifficulty(double starDifficulty) => ColourUtils.SampleFromLinearGradient(new[] + public static readonly (float, Color4)[] STAR_DIFFICULTY_SPECTRUM = { - (0.1f, Color4Extensions.FromHex("aaaaaa")), + (0.0f, Color4Extensions.FromHex("4290fb")), (0.1f, Color4Extensions.FromHex("4290fb")), (1.25f, Color4Extensions.FromHex("4fc0ff")), (2.0f, Color4Extensions.FromHex("4fffd5")), @@ -38,7 +34,19 @@ namespace osu.Game.Graphics (6.7f, Color4Extensions.FromHex("6563de")), (7.7f, Color4Extensions.FromHex("18158e")), (9.0f, Color4.Black), - }, (float)Math.Round(starDifficulty, 2, MidpointRounding.AwayFromZero)); + (10.0f, Color4.Black), + }; + + /// + /// Retrieves the colour for a given point in the star range. + /// + public Color4 ForStarDifficulty(double starDifficulty, bool showGrayOnZero = true) + { + if (showGrayOnZero && starDifficulty < 0.1f) + return Color4Extensions.FromHex("aaaaaa"); + + return ColourUtils.SampleFromLinearGradient(STAR_DIFFICULTY_SPECTRUM, (float)Math.Round(starDifficulty, 2, MidpointRounding.AwayFromZero)); + } /// /// Retrieves the colour for a . From 74227e7b79afb165a49963f187b603863587fe6b Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 3 Apr 2025 04:55:34 -0400 Subject: [PATCH 28/58] Define standard font sizes --- osu.Game/Graphics/OsuFont.cs | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs index 7aa98ece95..b314c602f5 100644 --- a/osu.Game/Graphics/OsuFont.cs +++ b/osu.Game/Graphics/OsuFont.cs @@ -15,15 +15,65 @@ namespace osu.Game.Graphics /// public const float DEFAULT_FONT_SIZE = 16; + /// + /// Template font styles which should be preferred whenever possible for UI elements. + /// + public static class Style + { + /// + /// Equivalent to Torus with 32px size and semi-bold weight. + /// + public static FontUsage Title => GetFont(Typeface.TorusAlternate, size: 32, weight: FontWeight.Regular); + + /// + /// Torus with 28px size and semi-bold weight. + /// + public static FontUsage Subtitle => GetFont(size: 28, weight: FontWeight.Regular); + + /// + /// Torus with 22px size and bold weight. + /// + public static FontUsage Heading1 => GetFont(size: 22, weight: FontWeight.Bold); + + /// + /// Torus with 18px size and semi-bold weight. + /// + public static FontUsage Heading2 => GetFont(size: 18, weight: FontWeight.SemiBold); + + /// + /// Torus with 16px size and regular weight. + /// + public static FontUsage Body => GetFont(size: DEFAULT_FONT_SIZE, weight: FontWeight.Regular); + + /// + /// Torus with 14px size and regular weight. + /// + public static FontUsage Caption1 => GetFont(size: 14, weight: FontWeight.Regular); + + /// + /// Torus with 12px size and regular weight. + /// + public static FontUsage Caption2 => GetFont(size: 12, weight: FontWeight.Regular); + } + /// /// The default font. /// - public static FontUsage Default => GetFont(); + public static FontUsage Default => GetFont(weight: FontWeight.Medium); + /// + /// Font face for numeric display. + /// public static FontUsage Numeric => GetFont(Typeface.Venera, weight: FontWeight.Bold); + /// + /// Default font face for UI and game elements. + /// public static FontUsage Torus => GetFont(Typeface.Torus, weight: FontWeight.Regular); + /// + /// Default font face with alternate character set for headings and flair text. + /// public static FontUsage TorusAlternate => GetFont(Typeface.TorusAlternate, weight: FontWeight.Regular); public static FontUsage Inter => GetFont(Typeface.Inter, weight: FontWeight.Regular); From 08c17bdf9ec27f9ee8ab35f9c9c2a3ae93b517f3 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 07:45:10 -0400 Subject: [PATCH 29/58] Remove conditional in difficulty spectrum retrieval function Wiil be handled locally instead using the diff in https://github.com/ppy/osu/pull/32764#discussion_r2039384833 --- osu.Game/Graphics/OsuColour.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index dec16d65bd..5adecc7182 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -22,7 +22,7 @@ namespace osu.Game.Graphics public static readonly (float, Color4)[] STAR_DIFFICULTY_SPECTRUM = { - (0.0f, Color4Extensions.FromHex("4290fb")), + (0.1f, Color4Extensions.FromHex("aaaaaa")), (0.1f, Color4Extensions.FromHex("4290fb")), (1.25f, Color4Extensions.FromHex("4fc0ff")), (2.0f, Color4Extensions.FromHex("4fffd5")), @@ -40,13 +40,7 @@ namespace osu.Game.Graphics /// /// Retrieves the colour for a given point in the star range. /// - public Color4 ForStarDifficulty(double starDifficulty, bool showGrayOnZero = true) - { - if (showGrayOnZero && starDifficulty < 0.1f) - return Color4Extensions.FromHex("aaaaaa"); - - return ColourUtils.SampleFromLinearGradient(STAR_DIFFICULTY_SPECTRUM, (float)Math.Round(starDifficulty, 2, MidpointRounding.AwayFromZero)); - } + public Color4 ForStarDifficulty(double starDifficulty) => ColourUtils.SampleFromLinearGradient(STAR_DIFFICULTY_SPECTRUM, (float)Math.Round(starDifficulty, 2, MidpointRounding.AwayFromZero)); /// /// Retrieves the colour for a . From c4cfd3a148344665654ca651738d0f96a716b2af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Apr 2025 21:14:56 +0900 Subject: [PATCH 30/58] Fix some incorrect/lacking comments --- .../Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 83b385bb8e..3ee0be61b1 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; +using osuTK; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables @@ -20,7 +21,7 @@ namespace osu.Game.Beatmaps.Drawables public partial class BeatmapSetOnlineStatusPill : CircularContainer, IHasTooltip { /// - /// Whether to show as "unknownn" instead of fading out. + /// Whether to show as "unknown" instead of fading out. /// public bool ShowUnknownStatus { get; init; } @@ -104,9 +105,10 @@ namespace osu.Game.Beatmaps.Drawables return; } - // Only animate resizing if we already have a size. - // This avoids animating height from zero. - if (Width > 0) + // The autosize animation on this component is intended to animate horizontal sizing only. + // To avoid vertical autosize animating from zero to non-zero, only apply the duration + // after we have a valid size. + if (Height > 0) { AutoSizeDuration = (float)animation_duration; AutoSizeEasing = Easing.OutQuint; From 4b9873f03e656e03ca539d5850ca2e6c97fd80ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Apr 2025 21:19:01 +0900 Subject: [PATCH 31/58] Avoid performing colour fades when pill is not visible in the first place --- .../Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 3ee0be61b1..7b3067e8d6 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; -using osuTK; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables @@ -116,6 +115,10 @@ namespace osu.Game.Beatmaps.Drawables this.FadeIn(animation_duration, Easing.OutQuint); + // Handle the case where transition from hidden to non-hidden may cause + // a fade from a colour that doesn't make sense (due to not being able to see the previous colour). + double duration = Alpha > 0 ? animation_duration : 0; + Color4 statusTextColour; if (colourProvider != null) @@ -123,8 +126,8 @@ namespace osu.Game.Beatmaps.Drawables else statusTextColour = status == BeatmapOnlineStatus.Graveyard ? colours.GreySeaFoamLight : Color4.Black; - statusText.FadeColour(statusTextColour, animation_duration, Easing.OutQuint); - background.FadeColour(OsuColour.ForBeatmapSetOnlineStatus(Status) ?? colourProvider?.Light1 ?? colours.GreySeaFoamLighter, animation_duration, Easing.OutQuint); + statusText.FadeColour(statusTextColour, duration, Easing.OutQuint); + background.FadeColour(OsuColour.ForBeatmapSetOnlineStatus(Status) ?? colourProvider?.Light1 ?? colours.GreySeaFoamLighter, duration, Easing.OutQuint); statusText.Text = Status.GetLocalisableDescription().ToUpper(); } From 85556e0c3e29e3ccb12f7324db82d18ff949a7fa Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 08:31:03 -0400 Subject: [PATCH 32/58] Introduce numeric value in beatmap hit count statistics --- .../Beatmaps/CatchBeatmap.cs | 5 +++++ .../Beatmaps/ManiaBeatmap.cs | 3 +++ osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 5 +++++ .../Beatmaps/TaikoBeatmap.cs | 5 +++++ osu.Game/Beatmaps/BeatmapStatistic.cs | 17 ++++++++++++++++- 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs index 01cec1d815..e9d087929f 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -16,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int fruits = HitObjects.Count(s => s is Fruit); int juiceStreams = HitObjects.Count(s => s is JuiceStream); int bananaShowers = HitObjects.Count(s => s is BananaShower); + int sum = Math.Max(1, fruits + juiceStreams + bananaShowers); return new[] { @@ -24,18 +26,21 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Name = @"Fruits", Content = fruits.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), + Ratio = fruits / (float)sum, }, new BeatmapStatistic { Name = @"Juice Streams", Content = juiceStreams.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), + Ratio = juiceStreams / (float)sum, }, new BeatmapStatistic { Name = @"Banana Showers", Content = bananaShowers.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), + Ratio = Math.Min(bananaShowers / 10f, 1), } }; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 8ddcfa128a..16e1751e95 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { int notes = HitObjects.Count(s => s is Note); int holdNotes = HitObjects.Count(s => s is HoldNote); + int sum = Math.Max(1, notes + holdNotes); return new[] { @@ -44,12 +45,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Name = @"Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = notes.ToString(), + Ratio = notes / (float)sum, }, new BeatmapStatistic { Name = @"Hold Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = holdNotes.ToString(), + Ratio = holdNotes / (float)sum, }, }; } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index 730a194751..cc73f2860a 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps int circles = HitObjects.Count(c => c is HitCircle); int sliders = HitObjects.Count(s => s is Slider); int spinners = HitObjects.Count(s => s is Spinner); + int sum = Math.Max(1, circles + sliders + spinners); return new[] { @@ -23,18 +25,21 @@ namespace osu.Game.Rulesets.Osu.Beatmaps Name = "Circles", Content = circles.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), + Ratio = circles / (float)sum, }, new BeatmapStatistic { Name = "Sliders", Content = sliders.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), + Ratio = sliders / (float)sum, }, new BeatmapStatistic { Name = @"Spinners", Content = spinners.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), + Ratio = Math.Min(spinners / 10f, 1), } }; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs index 0781485ab8..ad4413d84a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps int hits = HitObjects.Count(s => s is Hit); int drumRolls = HitObjects.Count(s => s is DrumRoll); int swells = HitObjects.Count(s => s is Swell); + int sum = Math.Max(1, hits + drumRolls + swells); return new[] { @@ -23,18 +25,21 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Name = @"Hits", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = hits.ToString(), + Ratio = hits / (float)sum, }, new BeatmapStatistic { Name = @"Drumrolls", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = drumRolls.ToString(), + Ratio = drumRolls / (float)sum, }, new BeatmapStatistic { Name = @"Swells", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), Content = swells.ToString(), + Ratio = Math.Min(swells / 10f, 1), } }; } diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs index 13e0e4ad5e..6faf74d9c6 100644 --- a/osu.Game/Beatmaps/BeatmapStatistic.cs +++ b/osu.Game/Beatmaps/BeatmapStatistic.cs @@ -16,7 +16,22 @@ namespace osu.Game.Beatmaps /// public Func CreateIcon; - public string Content; + /// + /// The name of this statistic. + /// public LocalisableString Name; + + /// + /// The text representing the value of this statistic. + /// + public string Content; + + /// + /// The ratio of this statistic compared to other relevant statistics, or null if not applicable. + /// + /// + /// This is used to display a bar on top of the statistic with the given ratio. + /// + public float? Ratio; } } From a2a1ddaa79fe63bd0b97757b59009b6873182107 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 06:55:39 -0400 Subject: [PATCH 33/58] Increase group panel height Matches design and also because of the next commit which increases group label size to coexist visually with other panel types. --- osu.Game/Screens/SelectV2/PanelGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/SelectV2/PanelGroup.cs b/osu.Game/Screens/SelectV2/PanelGroup.cs index ecb64f4797..800d7a2d07 100644 --- a/osu.Game/Screens/SelectV2/PanelGroup.cs +++ b/osu.Game/Screens/SelectV2/PanelGroup.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.SelectV2 { public partial class PanelGroup : PanelBase { - public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT; + public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.2f; private Drawable chevronIcon = null!; private OsuSpriteText titleText = null!; From d9d3c93a9696e17d006684659ebaa39bd1e929bc Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 06:55:26 -0400 Subject: [PATCH 34/58] Use font specification in group panels --- osu.Game/Screens/SelectV2/PanelGroup.cs | 4 +++- osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/SelectV2/PanelGroup.cs b/osu.Game/Screens/SelectV2/PanelGroup.cs index 800d7a2d07..410b6c9e86 100644 --- a/osu.Game/Screens/SelectV2/PanelGroup.cs +++ b/osu.Game/Screens/SelectV2/PanelGroup.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.SelectV2 { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, + Font = OsuFont.Style.Heading2, + UseFullGlyphHeight = false, X = 10f, }, new CircularContainer @@ -69,7 +71,7 @@ namespace osu.Game.Screens.SelectV2 { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + Font = OsuFont.Style.Caption1.With(weight: FontWeight.Bold), // TODO: requires Carousel/CarouselItem-side implementation Text = "43", UseFullGlyphHeight = false, diff --git a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs index 0dc5a2f365..a238539102 100644 --- a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs +++ b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.SelectV2 { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + Font = OsuFont.Style.Caption1.With(weight: FontWeight.Bold), // TODO: requires Carousel/CarouselItem-side implementation Text = "43", UseFullGlyphHeight = false, From 12e35557a545d8a50fce35268fafed901cb918b0 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 06:57:20 -0400 Subject: [PATCH 35/58] Update group panel design to match latest iteration --- osu.Game/Screens/SelectV2/PanelGroup.cs | 64 ++++++++++++++++++++----- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/SelectV2/PanelGroup.cs b/osu.Game/Screens/SelectV2/PanelGroup.cs index 410b6c9e86..a5786b53c9 100644 --- a/osu.Game/Screens/SelectV2/PanelGroup.cs +++ b/osu.Game/Screens/SelectV2/PanelGroup.cs @@ -5,10 +5,12 @@ using System.Diagnostics; 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.Game.Graphics; +using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; @@ -20,27 +22,56 @@ namespace osu.Game.Screens.SelectV2 { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.2f; - private Drawable chevronIcon = null!; + private Drawable iconContainer = null!; private OsuSpriteText titleText = null!; + private TrianglesV2 triangles = null!; + private Box glow = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load() { Height = HEIGHT; - Icon = chevronIcon = new SpriteIcon + Icon = iconContainer = new Container { AlwaysPresent = true, - Icon = FontAwesome.Solid.ChevronDown, - Size = new Vector2(12), - Margin = new MarginPadding { Horizontal = 5f }, - X = 2f, - Colour = colourProvider.Background3, + RelativeSizeAxes = Axes.Y, + Child = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.ChevronDown, + Size = new Vector2(12), + Colour = colourProvider.Background3, + }, }; - Background = new Box + Background = new Container { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Dark1, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + triangles = new TrianglesV2 + { + RelativeSizeAxes = Axes.Both, + Thickness = 0.02f, + SpawnRatio = 0.6f, + Colour = ColourInfo.GradientHorizontal(colourProvider.Background6, colourProvider.Background5) + }, + glow = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + Colour = ColourInfo.GradientHorizontal(colourProvider.Highlight1, colourProvider.Highlight1.Opacity(0f)), + }, + }, }; AccentColour = colourProvider.Highlight1; Content.Children = new Drawable[] @@ -77,7 +108,7 @@ namespace osu.Game.Screens.SelectV2 UseFullGlyphHeight = false, } }, - } + }, }; } @@ -92,8 +123,15 @@ namespace osu.Game.Screens.SelectV2 { const float duration = 500; - chevronIcon.ResizeWidthTo(Expanded.Value ? 12f : 0f, duration, Easing.OutQuint); - chevronIcon.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); + iconContainer.ResizeWidthTo(Expanded.Value ? 20f : 5f, duration, Easing.OutQuint); + iconContainer.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); + + ColourInfo colour = Expanded.Value + ? ColourInfo.GradientHorizontal(colourProvider.Highlight1.Opacity(0.25f), colourProvider.Highlight1.Opacity(0f)) + : ColourInfo.GradientHorizontal(colourProvider.Background6, colourProvider.Background5); + + triangles.FadeColour(colour, duration, Easing.OutQuint); + glow.FadeTo(Expanded.Value ? 0.4f : 0, duration, Easing.OutQuint); } protected override void PrepareForUse() From d3f3c4f6d08303a30ffcefc5c9e182945b4f6fff Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 06:58:04 -0400 Subject: [PATCH 36/58] Update star rating group panel to look better --- osu.Game/Localisation/SongSelectStrings.cs | 15 +++ .../SelectV2/PanelGroupStarDifficulty.cs | 109 +++++++++++++----- 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/osu.Game/Localisation/SongSelectStrings.cs b/osu.Game/Localisation/SongSelectStrings.cs index e715ba8880..ecf68e33a8 100644 --- a/osu.Game/Localisation/SongSelectStrings.cs +++ b/osu.Game/Localisation/SongSelectStrings.cs @@ -54,6 +54,21 @@ namespace osu.Game.Localisation /// public static LocalisableString EditBeatmap => new TranslatableString(getKey(@"edit_beatmap"), @"Edit beatmap"); + /// + /// "Below 1 Star" + /// + public static LocalisableString BelowStar => new TranslatableString(getKey(@"below_star"), @"Below 1 Star"); + + /// + /// "1 Star" + /// + public static LocalisableString Star => new TranslatableString(getKey(@"star"), @"1 Star"); + + /// + /// "{0} Stars" + /// + public static LocalisableString Stars(int starNumber) => new TranslatableString(getKey(@"stars"), @"{0} Stars", starNumber); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs index a238539102..2fba25b3f0 100644 --- a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs +++ b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs @@ -5,17 +5,17 @@ using System.Diagnostics; 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.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; +using osu.Game.Localisation; namespace osu.Game.Screens.SelectV2 { @@ -27,28 +27,50 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - private Drawable chevronIcon = null!; + private Drawable iconContainer = null!; private Box contentBackground = null!; - private StarRatingDisplay starRatingDisplay = null!; - private StarCounter starCounter = null!; + private OsuSpriteText starRatingText = null!; + private TrianglesV2 triangles = null!; + private Box glow = null!; [BackgroundDependencyLoader] private void load() { Height = PanelGroup.HEIGHT; - Icon = chevronIcon = new SpriteIcon + Icon = iconContainer = new Container { AlwaysPresent = true, - Icon = FontAwesome.Solid.ChevronDown, - Size = new Vector2(12), - Margin = new MarginPadding { Horizontal = 5f }, - X = 2f, + RelativeSizeAxes = Axes.Y, + Child = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.ChevronDown, + Size = new Vector2(12), + }, }; - Background = contentBackground = new Box + Background = new Container { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Dark1, + Children = new Drawable[] + { + contentBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + triangles = new TrianglesV2 + { + RelativeSizeAxes = Axes.Both, + Thickness = 0.02f, + SpawnRatio = 0.6f, + }, + glow = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0.5f, + }, + }, }; AccentColour = colourProvider.Highlight1; Content.Children = new Drawable[] @@ -62,17 +84,13 @@ namespace osu.Game.Screens.SelectV2 Margin = new MarginPadding { Left = 10f }, Children = new Drawable[] { - starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small) + starRatingText = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - }, - starCounter = new StarCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(8f / 20f), - }, + UseFullGlyphHeight = false, + Font = OsuFont.Style.Heading2, + } } }, new CircularContainer @@ -110,6 +128,8 @@ namespace osu.Game.Screens.SelectV2 Expanded.BindValueChanged(_ => onExpanded(), true); } + private Color4 ratingColour; + protected override void PrepareForUse() { base.PrepareForUse(); @@ -118,25 +138,52 @@ namespace osu.Game.Screens.SelectV2 int starNumber = (int)((GroupDefinition)Item.Model).Data; - Color4 colour = starNumber >= 9 ? OsuColour.Gray(0.2f) : colours.ForStarDifficulty(starNumber); - Color4 contentColour = starNumber >= 7 ? colours.Orange1 : colourProvider.Background5; + ratingColour = starNumber >= 9 ? OsuColour.Gray(0.2f) : colours.ForStarDifficulty(starNumber); - AccentColour = colour; - contentBackground.Colour = colour.Darken(0.3f); + AccentColour = ratingColour; + contentBackground.Colour = ratingColour.Darken(1f); + glow.Colour = ColourInfo.GradientHorizontal(ratingColour, ratingColour.Opacity(0f)); - starRatingDisplay.Current.Value = new StarDifficulty(starNumber, 0); - starCounter.Current = starNumber; + switch (starNumber) + { + case 0: + starRatingText.Text = SongSelectStrings.BelowStar; + break; - chevronIcon.Colour = contentColour; - starCounter.Colour = contentColour; + case 1: + starRatingText.Text = SongSelectStrings.Star; + break; + + default: + starRatingText.Text = SongSelectStrings.Stars(starNumber); + break; + } + + iconContainer.Colour = starNumber >= 7 ? colourProvider.Content1 : colourProvider.Background5; + starRatingText.Colour = colourProvider.Content1; + + ColourInfo colour; + + if (starNumber >= 8) + colour = ColourInfo.GradientHorizontal(ratingColour, ratingColour.Darken(0.2f)); + else + colour = ColourInfo.GradientHorizontal(ratingColour.Darken(0.6f), ratingColour.Darken(0.8f)); + + triangles.Colour = colour; + + onExpanded(); } private void onExpanded() { const float duration = 500; - chevronIcon.ResizeWidthTo(Expanded.Value ? 12f : 0f, duration, Easing.OutQuint); - chevronIcon.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); + Debug.Assert(Item != null); + + iconContainer.ResizeWidthTo(Expanded.Value ? 20f : 5f, duration, Easing.OutQuint); + iconContainer.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); + + glow.FadeTo(Expanded.Value ? 0.4f : 0, duration, Easing.OutQuint); } } } From 6db73b8a13146d05d57254d3a9029eb6a1cd8806 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 07:10:54 -0400 Subject: [PATCH 37/58] Change keyboard selection highlight colour to give betetr visuals --- osu.Game/Screens/SelectV2/PanelBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/SelectV2/PanelBase.cs b/osu.Game/Screens/SelectV2/PanelBase.cs index 05a1a55c03..32da02a189 100644 --- a/osu.Game/Screens/SelectV2/PanelBase.cs +++ b/osu.Game/Screens/SelectV2/PanelBase.cs @@ -159,7 +159,7 @@ namespace osu.Game.Screens.SelectV2 keyboardSelectionLayer = new Box { Alpha = 0, - Colour = colours.Yellow.Opacity(0.1f), + Colour = colourProvider.Highlight1.Opacity(0.1f), Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, }, From b95b9b36430fd5213bdf717c68308c45ac5f079a Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Fri, 11 Apr 2025 06:58:10 -0400 Subject: [PATCH 38/58] Improve group panel test scene --- .../TestSceneBeatmapCarouselV2GroupPanel.cs | 90 +++++++++++++------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs index 9b07f01e52..d62aee77f3 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; using osu.Game.Screens.SelectV2; using osu.Game.Tests.Visual.UserInterface; using osuTK; @@ -16,6 +19,66 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { } + [Test] + public void TestGeneral() + { + AddStep("general", () => CreateThemedContent(OverlayColourScheme.Aquamarine)); + } + + [Test] + public void TestStars() + { + for (int i = 0; i <= 10; i++) + { + int star = i; + + AddStep($"display {i} star(s)", () => + { + ContentContainer.Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Aquamarine)) + }, + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 0.5f, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 5f), + Children = new[] + { + new PanelGroupStarDifficulty + { + Item = new CarouselItem(new GroupDefinition(star, star.ToString())) + }, + new PanelGroupStarDifficulty + { + Item = new CarouselItem(new GroupDefinition(star, star.ToString())), + KeyboardSelected = { Value = true }, + }, + new PanelGroupStarDifficulty + { + Item = new CarouselItem(new GroupDefinition(star, star.ToString())), + Expanded = { Value = true }, + }, + new PanelGroupStarDifficulty + { + Item = new CarouselItem(new GroupDefinition(star, star.ToString())), + Expanded = { Value = true }, + KeyboardSelected = { Value = true }, + }, + }, + } + }; + }); + } + } + protected override Drawable CreateContent() { return new FillFlowContainer @@ -49,33 +112,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 KeyboardSelected = { Value = true }, Expanded = { Value = true } }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(1, "1")) - }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(3, "3")), - Expanded = { Value = true } - }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(5, "5")), - }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(7, "7")), - Expanded = { Value = true } - }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(8, "8")), - }, - new PanelGroupStarDifficulty - { - Item = new CarouselItem(new GroupDefinition(9, "9")), - Expanded = { Value = true } - }, } }; } From 76b94884b824a7665adfdc2b483c9ab67c3f507b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 13 Apr 2025 15:31:40 +0900 Subject: [PATCH 39/58] Remove localisation support for now The plural handling doesn't cover other languages so it's a bit pointless to localise in this manner. --- osu.Game/Localisation/SongSelectStrings.cs | 15 --------------- .../Screens/SelectV2/PanelGroupStarDifficulty.cs | 7 +++---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/osu.Game/Localisation/SongSelectStrings.cs b/osu.Game/Localisation/SongSelectStrings.cs index ecf68e33a8..e715ba8880 100644 --- a/osu.Game/Localisation/SongSelectStrings.cs +++ b/osu.Game/Localisation/SongSelectStrings.cs @@ -54,21 +54,6 @@ namespace osu.Game.Localisation /// public static LocalisableString EditBeatmap => new TranslatableString(getKey(@"edit_beatmap"), @"Edit beatmap"); - /// - /// "Below 1 Star" - /// - public static LocalisableString BelowStar => new TranslatableString(getKey(@"below_star"), @"Below 1 Star"); - - /// - /// "1 Star" - /// - public static LocalisableString Star => new TranslatableString(getKey(@"star"), @"1 Star"); - - /// - /// "{0} Stars" - /// - public static LocalisableString Stars(int starNumber) => new TranslatableString(getKey(@"stars"), @"{0} Stars", starNumber); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs index 2fba25b3f0..7353fd4095 100644 --- a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs +++ b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; using osuTK.Graphics; -using osu.Game.Localisation; namespace osu.Game.Screens.SelectV2 { @@ -147,15 +146,15 @@ namespace osu.Game.Screens.SelectV2 switch (starNumber) { case 0: - starRatingText.Text = SongSelectStrings.BelowStar; + starRatingText.Text = @"Below 1 Star"; break; case 1: - starRatingText.Text = SongSelectStrings.Star; + starRatingText.Text = @"1 Star"; break; default: - starRatingText.Text = SongSelectStrings.Stars(starNumber); + starRatingText.Text = $"{starNumber} Stars"; break; } From f79f427547de4273154e37108685e449f66be71d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 13 Apr 2025 15:34:59 +0900 Subject: [PATCH 40/58] Remove unnecessary assert --- osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs index 7353fd4095..ce46362133 100644 --- a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs +++ b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs @@ -177,8 +177,6 @@ namespace osu.Game.Screens.SelectV2 { const float duration = 500; - Debug.Assert(Item != null); - iconContainer.ResizeWidthTo(Expanded.Value ? 20f : 5f, duration, Easing.OutQuint); iconContainer.FadeTo(Expanded.Value ? 1f : 0f, duration, Easing.OutQuint); From 7cdbc2c20add6e8a41db071352812f8ee442a4fd Mon Sep 17 00:00:00 2001 From: Rudi Herouard Date: Sun, 13 Apr 2025 23:35:44 +0200 Subject: [PATCH 41/58] Fix "spins per minute" shows up early #31173 Make isSpinnableTime public in SpinnerRotationTracker and use it to set Tracking in OsuModSpunOut. Tracking was previously set to true, causing the "spins per minute" to appear immediately when the spinner appeared. --- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- .../Skinning/Default/SpinnerRotationTracker.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 992f4d5f03..222cf4242a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var spinner = (DrawableSpinner)drawable; - spinner.RotationTracker.Tracking = true; + spinner.RotationTracker.Tracking = spinner.RotationTracker.IsSpinnableTime; // early-return if we were paused to avoid division-by-zero in the subsequent calculations. if (Precision.AlmostEquals(spinner.Clock.Rate, 0)) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs index 7e97f826f9..7cd1f39871 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default /// /// Whether currently in the correct time range to allow spinning. /// - private bool isSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current; + public bool IsSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current; protected override bool OnMouseMove(MouseMoveEvent e) { @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default lastAngle = thisAngle; } - IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation - Rotation) > 10f; + IsSpinning.Value = IsSpinnableTime && Math.Abs(currentRotation - Rotation) > 10f; Rotation = (float)Interpolation.Damp(Rotation, currentRotation, 0.99, Math.Abs(Time.Elapsed)); } @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default /// The delta angle. public void AddRotation(float delta) { - if (!isSpinnableTime) + if (!IsSpinnableTime) return; if (!rotationTransferred) From f4cb3a7fb3e4217379af0d4ab7ec44269beed718 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Apr 2025 13:54:11 +0900 Subject: [PATCH 42/58] Add support for closing chat channels with middle click Closes https://github.com/ppy/osu/issues/32797. --- .../Visual/Online/TestSceneChatOverlay.cs | 26 +++++++++++++++++++ .../Chat/ChannelList/ChannelListItem.cs | 12 +++++++++ 2 files changed, 38 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index ab9ee1d8cc..d0fc66252e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -215,6 +215,32 @@ namespace osu.Game.Tests.Visual.Online }); } + [Test] + public void TestChannelCloseViaMiddleClick() + { + var testPMChannel = new Channel(testUser); + + AddStep("Show overlay", () => chatOverlay.Show()); + joinTestChannel(0); + joinChannel(testPMChannel); + AddStep("Select PM channel", () => clickDrawable(getChannelListItem(testPMChannel))); + AddStep("Middle click", () => + { + var item = getChannelListItem(testPMChannel); + InputManager.MoveMouseTo(item); + InputManager.Click(MouseButton.Middle); + }); + AddAssert("PM channel closed", () => !channelManager.JoinedChannels.Contains(testPMChannel)); + AddStep("Select normal channel", () => clickDrawable(getChannelListItem(testChannel1))); + AddStep("Click close button", () => + { + var item = getChannelListItem(testChannel1); + InputManager.MoveMouseTo(item); + InputManager.Click(MouseButton.Middle); + }); + AddAssert("Normal channel closed", () => !channelManager.JoinedChannels.Contains(testChannel1)); + } + [Test] public void TestChannelCloseButton() { diff --git a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs index 6107f130ec..3741852993 100644 --- a/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelList/ChannelListItem.cs @@ -18,6 +18,7 @@ using osu.Game.Online.Chat; using osu.Game.Overlays.Chat.Listing; using osu.Game.Users.Drawables; using osuTK; +using osuTK.Input; namespace osu.Game.Overlays.Chat.ChannelList { @@ -160,6 +161,17 @@ namespace osu.Game.Overlays.Chat.ChannelList }; } + protected override bool OnMouseDown(MouseDownEvent e) + { + if (e.Button == MouseButton.Middle) + { + close?.TriggerClick(); + return true; + } + + return base.OnMouseDown(e); + } + private ChannelListItemCloseButton? createCloseButton() { if (isSelector || !CanLeave) From 80d9f742da7b9efa0f98ca7715351bcc427e8a70 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 14 Apr 2025 17:45:15 +0900 Subject: [PATCH 43/58] Combine "spinnable time" conditions --- .../Objects/Drawables/DrawableSpinner.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 8c21e6a6bc..64cedd216b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -277,13 +277,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.Update(); if (HandleUserInput) - { - bool isValidSpinningTime = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime; - - RotationTracker.Tracking = !Result.HasResult - && correctButtonPressed() - && isValidSpinningTime; - } + RotationTracker.Tracking = RotationTracker.IsSpinnableTime && !Result.HasResult && correctButtonPressed(); if (spinningSample != null && spinnerFrequencyModulate) spinningSample.Frequency.Value = spinning_sample_modulated_base_frequency + Progress; From f05a50f4e19fa215dfd761b9de328945fbe1bd25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Apr 2025 18:38:04 +0900 Subject: [PATCH 44/58] Rename new property to better explain visual-only usage --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs | 6 +++--- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs | 4 ++-- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 6 +++--- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs | 6 +++--- osu.Game/Beatmaps/BeatmapStatistic.cs | 7 ++----- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs index e9d087929f..eadf7f42bc 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -26,21 +26,21 @@ namespace osu.Game.Rulesets.Catch.Beatmaps Name = @"Fruits", Content = fruits.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), - Ratio = fruits / (float)sum, + BarDisplayLength = fruits / (float)sum, }, new BeatmapStatistic { Name = @"Juice Streams", Content = juiceStreams.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), - Ratio = juiceStreams / (float)sum, + BarDisplayLength = juiceStreams / (float)sum, }, new BeatmapStatistic { Name = @"Banana Showers", Content = bananaShowers.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), - Ratio = Math.Min(bananaShowers / 10f, 1), + BarDisplayLength = Math.Min(bananaShowers / 10f, 1), } }; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs index 16e1751e95..3ee1b63800 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs @@ -45,14 +45,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Name = @"Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = notes.ToString(), - Ratio = notes / (float)sum, + BarDisplayLength = notes / (float)sum, }, new BeatmapStatistic { Name = @"Hold Notes", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = holdNotes.ToString(), - Ratio = holdNotes / (float)sum, + BarDisplayLength = holdNotes / (float)sum, }, }; } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index cc73f2860a..2600f63ab9 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -25,21 +25,21 @@ namespace osu.Game.Rulesets.Osu.Beatmaps Name = "Circles", Content = circles.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), - Ratio = circles / (float)sum, + BarDisplayLength = circles / (float)sum, }, new BeatmapStatistic { Name = "Sliders", Content = sliders.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), - Ratio = sliders / (float)sum, + BarDisplayLength = sliders / (float)sum, }, new BeatmapStatistic { Name = @"Spinners", Content = spinners.ToString(), CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), - Ratio = Math.Min(spinners / 10f, 1), + BarDisplayLength = Math.Min(spinners / 10f, 1), } }; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs index ad4413d84a..e8cd05ee73 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs @@ -25,21 +25,21 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps Name = @"Hits", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles), Content = hits.ToString(), - Ratio = hits / (float)sum, + BarDisplayLength = hits / (float)sum, }, new BeatmapStatistic { Name = @"Drumrolls", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders), Content = drumRolls.ToString(), - Ratio = drumRolls / (float)sum, + BarDisplayLength = drumRolls / (float)sum, }, new BeatmapStatistic { Name = @"Swells", CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners), Content = swells.ToString(), - Ratio = Math.Min(swells / 10f, 1), + BarDisplayLength = Math.Min(swells / 10f, 1), } }; } diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs index 6faf74d9c6..64e42f3f02 100644 --- a/osu.Game/Beatmaps/BeatmapStatistic.cs +++ b/osu.Game/Beatmaps/BeatmapStatistic.cs @@ -27,11 +27,8 @@ namespace osu.Game.Beatmaps public string Content; /// - /// The ratio of this statistic compared to other relevant statistics, or null if not applicable. + /// The length of a bar which visually represents this statistic's relevance in the beatmap. /// - /// - /// This is used to display a bar on top of the statistic with the given ratio. - /// - public float? Ratio; + public float? BarDisplayLength; } } From 3d47a2b5b2b94a92cbd757b705561519ad3c93d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 14 Apr 2025 19:21:36 +0900 Subject: [PATCH 45/58] Don't include spinner types in note sums --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs | 2 +- osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs index eadf7f42bc..d43290e661 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int fruits = HitObjects.Count(s => s is Fruit); int juiceStreams = HitObjects.Count(s => s is JuiceStream); int bananaShowers = HitObjects.Count(s => s is BananaShower); - int sum = Math.Max(1, fruits + juiceStreams + bananaShowers); + int sum = Math.Max(1, fruits + juiceStreams); return new[] { diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs index 2600f63ab9..d11b4aac3b 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps int circles = HitObjects.Count(c => c is HitCircle); int sliders = HitObjects.Count(s => s is Slider); int spinners = HitObjects.Count(s => s is Spinner); - int sum = Math.Max(1, circles + sliders + spinners); + int sum = Math.Max(1, circles + sliders); return new[] { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs index e8cd05ee73..5b0582ab59 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps int hits = HitObjects.Count(s => s is Hit); int drumRolls = HitObjects.Count(s => s is DrumRoll); int swells = HitObjects.Count(s => s is Swell); - int sum = Math.Max(1, hits + drumRolls + swells); + int sum = Math.Max(1, hits + drumRolls); return new[] { From e8af0dabea0aab5e171f66308080b3f6ae0ff9d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 15:53:20 +0900 Subject: [PATCH 46/58] Fix thread safety when calling `BeatmapStore.GetBeatmapSets` While usually we'd handle this locally by moving bind operations to `LoadComponent`, this component was explicitly made to be used in asynchronous scenarios (to allow cases like song select to coexist with realm without adding huge compliexities to the classes locally). So I think it makes sense to hide this as an implementation detail. The locked segments should all be quite fast to run so I do not see a performance issue with lock contention here. --- .../Database/RealmDetachedBeatmapStore.cs | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/osu.Game/Database/RealmDetachedBeatmapStore.cs b/osu.Game/Database/RealmDetachedBeatmapStore.cs index b05e07ef31..6954bb320a 100644 --- a/osu.Game/Database/RealmDetachedBeatmapStore.cs +++ b/osu.Game/Database/RealmDetachedBeatmapStore.cs @@ -30,7 +30,8 @@ namespace osu.Game.Database public override IBindableList GetBeatmapSets(CancellationToken? cancellationToken) { loaded.Wait(cancellationToken ?? CancellationToken.None); - return detachedBeatmapSets.GetBoundCopy(); + lock (detachedBeatmapSets) + return detachedBeatmapSets.GetBoundCopy(); } [BackgroundDependencyLoader] @@ -65,8 +66,11 @@ namespace osu.Game.Database { var detached = frozenSets.Detach(); - detachedBeatmapSets.Clear(); - detachedBeatmapSets.AddRange(detached); + lock (detachedBeatmapSets) + { + detachedBeatmapSets.Clear(); + detachedBeatmapSets.AddRange(detached); + } }); } finally @@ -116,22 +120,28 @@ namespace osu.Game.Database if (!loaded.IsSet) return; - // If this ever leads to performance issues, we could dequeue a limited number of operations per update frame. - while (pendingOperations.TryDequeue(out var op)) + if (pendingOperations.Count == 0) + return; + + lock (detachedBeatmapSets) { - switch (op.Type) + // If this ever leads to performance issues, we could dequeue a limited number of operations per update frame. + while (pendingOperations.TryDequeue(out var op)) { - case OperationType.Insert: - detachedBeatmapSets.Insert(op.Index, op.BeatmapSet!); - break; + switch (op.Type) + { + case OperationType.Insert: + detachedBeatmapSets.Insert(op.Index, op.BeatmapSet!); + break; - case OperationType.Update: - detachedBeatmapSets.ReplaceRange(op.Index, 1, new[] { op.BeatmapSet! }); - break; + case OperationType.Update: + detachedBeatmapSets.ReplaceRange(op.Index, 1, new[] { op.BeatmapSet! }); + break; - case OperationType.Remove: - detachedBeatmapSets.RemoveAt(op.Index); - break; + case OperationType.Remove: + detachedBeatmapSets.RemoveAt(op.Index); + break; + } } } } From 64b9d4642adb42db7b619f76ecf5c028d9d1c3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 16 Apr 2025 10:27:05 +0200 Subject: [PATCH 47/58] Add failing test --- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index ebeba23123..45381b3e02 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -115,6 +115,27 @@ namespace osu.Game.Tests.Visual.SongSelect checkDisplayedCount(0); } + [Test] + public void TestLocalScoresDisplayWorksWhenStartingOffline() + { + BeatmapInfo beatmapInfo = null!; + + AddStep("Log out", () => API.Logout()); + AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local); + + AddStep(@"Set beatmap", () => + { + beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely(); + beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + + leaderboard.BeatmapInfo = beatmapInfo; + }); + + clearScores(); + importMoreScores(() => beatmapInfo); + checkDisplayedCount(10); + } + [Test] public void TestLocalScoresDisplayOnBeatmapEdit() { From cf2f6d7233a1a12f44024656db5c482068e8dc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 16 Apr 2025 10:27:33 +0200 Subject: [PATCH 48/58] Fix local leaderboards not showing when starting game offline Broke in https://github.com/ppy/osu/pull/32494. Oops. --- osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index c52cd61c42..2896e7eab4 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Select.Leaderboards var fetchRuleset = ruleset.Value ?? fetchBeatmapInfo.Ruleset; - if (!api.IsLoggedIn) + if (!api.IsLoggedIn && IsOnlineScope) { SetErrorState(LeaderboardState.NotLoggedIn); return null; From 7a8e96f3223618315669120de68163e21bbf384b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 16:54:01 +0900 Subject: [PATCH 49/58] Change global shear definition to be a `Vector2` Saves having this defined in 20+ places. If we ever make any changes to shear, it's 100% going to need to be applied to every usage (there will never be a case of multiple different shears in the game). Also fixes a mismatching definition in `ShearedNub`. --- .../SongSelectV2/TestSceneLeaderboardScore.cs | 4 ++-- .../Graphics/UserInterface/DialogButton.cs | 4 ++-- .../Graphics/UserInterface/ShearedButton.cs | 9 +++------ osu.Game/Graphics/UserInterface/ShearedNub.cs | 4 +--- .../UserInterface/ShearedSearchTextBox.cs | 4 ++-- .../UserInterface/ShearedSliderBar.cs | 6 +++--- .../UserInterfaceV2/ShearedDropdown.cs | 20 ++++++------------- osu.Game/OsuGame.cs | 2 +- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 15 ++++++-------- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- .../Mods/ModFooterInformationDisplay.cs | 2 +- osu.Game/Overlays/Mods/ModPanel.cs | 2 +- osu.Game/Overlays/Mods/ModSelectColumn.cs | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- osu.Game/Overlays/Mods/ModSelectPanel.cs | 9 ++++----- .../Mods/RankingInformationDisplay.cs | 7 +++---- osu.Game/Screens/Footer/ScreenFooterButton.cs | 10 +++------- .../DailyChallenge/DailyChallengeIntro.cs | 18 ++++++++--------- .../Select/Options/BeatmapOptionsButton.cs | 2 +- .../SelectV2/Footer/ScreenFooterButtonMods.cs | 12 +++++------ .../Leaderboards/LeaderboardScoreV2.cs | 20 +++++++++---------- .../SelectV2/UpdateBeatmapSetButton.cs | 4 ++-- 22 files changed, 72 insertions(+), 94 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs index 26d39c9203..08c0c92285 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 2f), - Shear = new Vector2(OsuGame.SHEAR, 0) + Shear = OsuGame.SHEAR, }, drawWidthText = new OsuSpriteText(), }; @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 2f), - Shear = new Vector2(OsuGame.SHEAR, 0) + Shear = OsuGame.SHEAR, }, drawWidthText = new OsuSpriteText(), }; diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index c39f41bf72..423d9637b8 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -129,7 +129,7 @@ namespace osu.Game.Graphics.UserInterface Radius = 5, }, Colour = ButtonColour, - Shear = new Vector2(0.2f, 0), + Shear = OsuGame.SHEAR, Children = new Drawable[] { new Box @@ -149,7 +149,7 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both, TriangleScale = 4, ColourDark = OsuColour.Gray(0.88f), - Shear = new Vector2(-0.2f, 0), + Shear = -OsuGame.SHEAR, ClampAxes = Axes.Y }, }, diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index 87d269ccd4..a059490aa8 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -11,7 +11,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; -using osuTK; namespace osu.Game.Graphics.UserInterface { @@ -66,8 +65,6 @@ namespace osu.Game.Graphics.UserInterface private readonly Box background; private readonly OsuSpriteText text; - private const float shear = OsuGame.SHEAR; - private Colour4? darkerColour; private Colour4? lighterColour; private Colour4? textColour; @@ -91,10 +88,10 @@ namespace osu.Game.Graphics.UserInterface public ShearedButton(float? width = null, float height = DEFAULT_HEIGHT) { Height = height; - Padding = new MarginPadding { Horizontal = shear * height }; + Padding = new MarginPadding { Horizontal = OsuGame.SHEAR.X * height }; Content.CornerRadius = CORNER_RADIUS; - Content.Shear = new Vector2(shear, 0); + Content.Shear = OsuGame.SHEAR; Content.Masking = true; Content.Anchor = Content.Origin = Anchor.Centre; @@ -117,7 +114,7 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, - Shear = new Vector2(-shear, 0), + Shear = -OsuGame.SHEAR, Child = text = new OsuSpriteText { Font = OsuFont.TorusAlternate.With(size: 17), diff --git a/osu.Game/Graphics/UserInterface/ShearedNub.cs b/osu.Game/Graphics/UserInterface/ShearedNub.cs index 7485f68525..17b50b5d58 100644 --- a/osu.Game/Graphics/UserInterface/ShearedNub.cs +++ b/osu.Game/Graphics/UserInterface/ShearedNub.cs @@ -26,8 +26,6 @@ namespace osu.Game.Graphics.UserInterface public const int HEIGHT = 30; public const float EXPANDED_SIZE = 50; - public static readonly Vector2 SHEAR = new Vector2(0.15f, 0); - private readonly Box fill; private readonly Container main; @@ -40,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface Size = new Vector2(EXPANDED_SIZE, HEIGHT); InternalChild = main = new Container { - Shear = SHEAR, + Shear = OsuGame.SHEAR, BorderColour = Colour4.White, BorderThickness = BORDER_WIDTH, Masking = true, diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index c6565726b5..f5fbb3411f 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.UserInterface public ShearedSearchTextBox() { Height = 42; - Shear = new Vector2(OsuGame.SHEAR, 0); + Shear = OsuGame.SHEAR; Masking = true; CornerRadius = corner_radius; @@ -115,7 +115,7 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = CommonStrings.InputSearch; CornerRadius = corner_radius; - TextContainer.Shear = new Vector2(-OsuGame.SHEAR, 0); + TextContainer.Shear = -OsuGame.SHEAR; } protected override SpriteText CreatePlaceholder() => new SearchPlaceholder(); diff --git a/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs b/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs index a36b9c7a4c..e7b57f5c9e 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSliderBar.cs @@ -58,7 +58,7 @@ namespace osu.Game.Graphics.UserInterface public ShearedSliderBar() { - Shear = SHEAR; + Shear = OsuGame.SHEAR; Height = HEIGHT; RangePadding = EXPANDED_SIZE / 2; Children = new Drawable[] @@ -98,11 +98,11 @@ namespace osu.Game.Graphics.UserInterface }, nubContainer = new Container { - Shear = -SHEAR, + Shear = -OsuGame.SHEAR, RelativeSizeAxes = Axes.Both, Child = Nub = new ShearedNub { - X = -SHEAR.X * HEIGHT / 2f, + X = -OsuGame.SHEAR.X * HEIGHT / 2f, Origin = Anchor.TopCentre, RelativePositionAxes = Axes.X, Current = { Value = true }, diff --git a/osu.Game/Graphics/UserInterfaceV2/ShearedDropdown.cs b/osu.Game/Graphics/UserInterfaceV2/ShearedDropdown.cs index 0b9c5f294c..609f77dd7e 100644 --- a/osu.Game/Graphics/UserInterfaceV2/ShearedDropdown.cs +++ b/osu.Game/Graphics/UserInterfaceV2/ShearedDropdown.cs @@ -62,8 +62,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected partial class ShearedDropdownMenu : OsuDropdown.OsuDropdownMenu { - private readonly Vector2 shear = new Vector2(OsuGame.SHEAR, 0); - public new MarginPadding Padding { get => base.Padding; @@ -72,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 public ShearedDropdownMenu() { - Shear = shear; + Shear = OsuGame.SHEAR; Margin = new MarginPadding { Top = 5f }; } @@ -84,12 +82,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 public partial class ShearedMenuItem : DrawableOsuDropdownMenuItem { - private readonly Vector2 shear = new Vector2(OsuGame.SHEAR, 0); - public ShearedMenuItem(MenuItem item) : base(item) { - Foreground.Shear = -shear; + Foreground.Shear = -OsuGame.SHEAR; } } } @@ -125,14 +121,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 public ShearedDropdown Dropdown = null!; private ShearedDropdownSearchBar searchBar = null!; - private readonly Vector2 shear = new Vector2(OsuGame.SHEAR, 0); - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; public ShearedDropdownHeader() { - Shear = shear; + Shear = OsuGame.SHEAR; CornerRadius = corner_radius; Masking = true; @@ -167,7 +161,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 { Margin = new MarginPadding { Horizontal = 10f, Vertical = 8f }, Font = OsuFont.Torus.With(size: 16.8f, weight: FontWeight.SemiBold), - Shear = -shear, + Shear = -OsuGame.SHEAR, }, }, }, @@ -178,7 +172,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10f }, - Shear = -shear, + Shear = -OsuGame.SHEAR, Children = new Drawable[] { valueText = new TruncatingSpriteText @@ -286,12 +280,10 @@ namespace osu.Game.Graphics.UserInterfaceV2 private partial class DropdownSearchTextBox : OsuTextBox { - private readonly Vector2 shear = new Vector2(OsuGame.SHEAR, 0); - [BackgroundDependencyLoader] private void load(OverlayColourProvider? colourProvider) { - TextContainer.Shear = -shear; + TextContainer.Shear = -OsuGame.SHEAR; BackgroundUnfocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255); BackgroundFocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0c75a4106a..70a324cd8e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -102,7 +102,7 @@ namespace osu.Game /// /// A common shear factor applied to most components of the game. /// - public const float SHEAR = 0.2f; + public static readonly Vector2 SHEAR = new Vector2(0.2f, 0); public Toolbar Toolbar { get; private set; } diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index dedd1e336e..3cefa07cfa 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -20,7 +20,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Utils; -using osuTK; namespace osu.Game.Overlays.Mods { @@ -66,21 +65,19 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { - const float shear = OsuGame.SHEAR; - LeftContent.AddRange(new Drawable[] { starRatingDisplay = new StarRatingDisplay(default, animated: true) { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Shear = new Vector2(-shear, 0), + Shear = -OsuGame.SHEAR, }, bpmDisplay = new BPMDisplay { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Shear = new Vector2(-shear, 0), + Shear = -OsuGame.SHEAR, AutoSizeAxes = Axes.Y, Width = 75, } @@ -89,10 +86,10 @@ namespace osu.Game.Overlays.Mods RightContent.Alpha = 0; RightContent.AddRange(new Drawable[] { - circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), }, - drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), }, - overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), }, - approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, + circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = -OsuGame.SHEAR, }, + drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = -OsuGame.SHEAR, }, + overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = -OsuGame.SHEAR, }, + approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = -OsuGame.SHEAR, }, }); } diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 326394a207..7d2ce54074 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -106,7 +106,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft, Scale = new Vector2(0.8f), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-OsuGame.SHEAR, 0) + Shear = -OsuGame.SHEAR }); ItemsFlow.Padding = new MarginPadding { diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs index 6665a3b8dc..db42200775 100644 --- a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, Height = ShearedButton.DEFAULT_HEIGHT, - Shear = new Vector2(OsuGame.SHEAR, 0), + Shear = OsuGame.SHEAR, CornerRadius = ShearedButton.CORNER_RADIUS, BorderThickness = ShearedButton.BORDER_THICKNESS, Masking = true, diff --git a/osu.Game/Overlays/Mods/ModPanel.cs b/osu.Game/Overlays/Mods/ModPanel.cs index b85904f22b..df72692f48 100644 --- a/osu.Game/Overlays/Mods/ModPanel.cs +++ b/osu.Game/Overlays/Mods/ModPanel.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Active = { BindTarget = Active }, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE) }; } diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 8a499a391c..92c75e3494 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Mods { Width = WIDTH; RelativeSizeAxes = Axes.Y; - Shear = new Vector2(OsuGame.SHEAR, 0); + Shear = OsuGame.SHEAR; InternalChildren = new Drawable[] { @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.X, Height = header_height, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Velocity = 0.7f, ClampAxes = Axes.Y }, @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Mods AutoSizeAxes = Axes.Y, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Padding = new MarginPadding { Horizontal = 17, diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d36092ebed..9ba3b3774f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Direction = FillDirection.Horizontal, - Shear = new Vector2(OsuGame.SHEAR, 0), + Shear = OsuGame.SHEAR, RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Margin = new MarginPadding { Horizontal = 70 }, @@ -726,7 +726,7 @@ namespace osu.Game.Overlays.Mods // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent); - var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * OsuGame.SHEAR, 0), ScrollContent); + var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * OsuGame.SHEAR.X, 0), ScrollContent); bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) && Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X); diff --git a/osu.Game/Overlays/Mods/ModSelectPanel.cs b/osu.Game/Overlays/Mods/ModSelectPanel.cs index 284356f37e..6d48576742 100644 --- a/osu.Game/Overlays/Mods/ModSelectPanel.cs +++ b/osu.Game/Overlays/Mods/ModSelectPanel.cs @@ -20,7 +20,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -87,7 +86,7 @@ namespace osu.Game.Overlays.Mods Content.CornerRadius = CORNER_RADIUS; Content.BorderThickness = 2; - Shear = new Vector2(OsuGame.SHEAR, 0); + Shear = OsuGame.SHEAR; Children = new Drawable[] { @@ -128,10 +127,10 @@ namespace osu.Game.Overlays.Mods { Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Margin = new MarginPadding { - Left = -18 * OsuGame.SHEAR + Left = -18 * OsuGame.SHEAR.X }, ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. }, @@ -139,7 +138,7 @@ namespace osu.Game.Overlays.Mods { Font = OsuFont.Default.With(size: 12), RelativeSizeAxes = Axes.X, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`. } } diff --git a/osu.Game/Overlays/Mods/RankingInformationDisplay.cs b/osu.Game/Overlays/Mods/RankingInformationDisplay.cs index 75a8f289d8..11c963f616 100644 --- a/osu.Game/Overlays/Mods/RankingInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/RankingInformationDisplay.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; using osu.Game.Utils; -using osuTK; namespace osu.Game.Overlays.Mods { @@ -52,7 +51,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, RelativeSizeAxes = Axes.Both, - Shear = new Vector2(OsuGame.SHEAR, 0), + Shear = OsuGame.SHEAR, CornerRadius = ShearedButton.CORNER_RADIUS, Masking = true, Children = new Drawable[] @@ -79,7 +78,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } } @@ -94,7 +93,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Child = counter = new EffectCounter { - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Anchor = Anchor.Centre, Origin = Anchor.Centre, Current = { BindTarget = ModMultiplier } diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index 6515203ca0..5e96eadfea 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -25,16 +25,12 @@ namespace osu.Game.Screens.Footer { public partial class ScreenFooterButton : OsuClickableContainer, IKeyBindingHandler { - private const float shear = OsuGame.SHEAR; - protected const int CORNER_RADIUS = 10; protected const int BUTTON_HEIGHT = 75; protected const int BUTTON_WIDTH = 116; public Bindable OverlayState = new Bindable(); - protected static readonly Vector2 BUTTON_SHEAR = new Vector2(shear, 0); - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -89,7 +85,7 @@ namespace osu.Game.Screens.Footer Colour = Colour4.Black.Opacity(0.25f), Offset = new Vector2(0, 2), }, - Shear = BUTTON_SHEAR, + Shear = OsuGame.SHEAR, Masking = true, CornerRadius = CORNER_RADIUS, RelativeSizeAxes = Axes.Both, @@ -108,7 +104,7 @@ namespace osu.Game.Screens.Footer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { @@ -135,7 +131,7 @@ namespace osu.Game.Screens.Footer }, new Container { - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, Anchor = Anchor.BottomCentre, Origin = Anchor.Centre, Y = -CORNER_RADIUS, diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs index 5b423fbc6d..3ec9217aa4 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeIntro.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = new Vector2(OsuGame.SHEAR, 0f), + Shear = OsuGame.SHEAR, Children = new Drawable[] { titleContainer = new Container @@ -147,7 +147,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Origin = Anchor.Centre, Text = "Today's Challenge", Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f }, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate), }, } @@ -173,7 +173,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Origin = Anchor.Centre, Text = room.Name.Split(':', StringSplitOptions.TrimEntries).Last(), Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f }, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate), }, } @@ -246,7 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, MaxWidth = horizontal_info_size, Text = beatmap.BeatmapSet!.Metadata.GetDisplayTitleRomanisable(false), Padding = new MarginPadding { Horizontal = 5f }, @@ -257,7 +257,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Text = $"Difficulty: {beatmap.DifficultyName}", Font = OsuFont.GetFont(size: 20, italics: true), MaxWidth = horizontal_info_size, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, @@ -266,13 +266,13 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Text = $"by {beatmap.Metadata.Author.Username}", Font = OsuFont.GetFont(size: 16, italics: true), MaxWidth = horizontal_info_size, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, starRatingDisplay = new StarRatingDisplay(default) { - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Margin = new MarginPadding(5), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -301,7 +301,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, Current = { Value = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray() @@ -329,7 +329,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Origin = Anchor.Centre, FillMode = FillMode.Fit, Scale = new Vector2(1.2f), - Shear = new Vector2(-OsuGame.SHEAR, 0f), + Shear = -OsuGame.SHEAR, }, c => { beatmapBackground.Add(c); diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 045a518525..572b2427b1 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Select.Options Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Shear = new Vector2(0.2f, 0f), + Shear = OsuGame.SHEAR, Masking = true, EdgeEffect = new EdgeEffectParameters { diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs index 869aef1470..61d69ae197 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.SelectV2.Footer Y = -5f, Depth = float.MaxValue, Origin = Anchor.BottomLeft, - Shear = BUTTON_SHEAR, + Shear = OsuGame.SHEAR, CornerRadius = CORNER_RADIUS, Size = new Vector2(BUTTON_WIDTH, bar_height), Masking = true, @@ -108,7 +108,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, UseFullGlyphHeight = false, Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold) } @@ -130,7 +130,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, Scale = new Vector2(0.5f), Current = { BindTarget = Current }, ExpansionMode = ExpansionMode.AlwaysContracted, @@ -139,7 +139,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold), Mods = { BindTarget = Current }, } @@ -305,7 +305,7 @@ namespace osu.Game.Screens.SelectV2.Footer Y = -5f; Depth = float.MaxValue; Origin = Anchor.BottomLeft; - Shear = BUTTON_SHEAR; + Shear = OsuGame.SHEAR; CornerRadius = CORNER_RADIUS; AutoSizeAxes = Axes.X; Height = bar_height; @@ -329,7 +329,7 @@ namespace osu.Game.Screens.SelectV2.Footer { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Shear = -BUTTON_SHEAR, + Shear = -OsuGame.SHEAR, Text = ModSelectOverlayStrings.Unranked.ToUpper(), Margin = new MarginPadding { Horizontal = 15 }, UseFullGlyphHeight = false, diff --git a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs index 16599a2080..0b7b2ebbc1 100644 --- a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs +++ b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards this.score = score; this.sheared = sheared; - Shear = new Vector2(sheared ? OsuGame.SHEAR : 0, 0); + Shear = sheared ? OsuGame.SHEAR : Vector2.Zero; RelativeSizeAxes = Axes.X; Height = height; } @@ -255,7 +255,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { RelativeSizeAxes = Axes.Both, User = score.User, - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Colour = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0.5f), Colour4.FromHex(@"222A27").Opacity(1)), @@ -286,7 +286,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(1.1f), - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, RelativeSizeAxes = Axes.Both, }) { @@ -326,7 +326,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { flagBadgeAndDateContainer = new FillFlowContainer { - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Direction = FillDirection.Horizontal, Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, @@ -356,7 +356,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards nameLabel = new TruncatingSpriteText { RelativeSizeAxes = Axes.X, - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Text = user.Username, Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) } @@ -372,7 +372,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Name = @"Statistics container", Padding = new MarginPadding { Right = 40 }, Spacing = new Vector2(25, 0), - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, @@ -430,7 +430,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards }, RankContainer = new Container { - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Y, @@ -488,7 +488,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Anchor = Anchor.TopRight, Origin = Anchor.TopRight, UseFullGlyphHeight = false, - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Current = scoreManager.GetBindableTotalScoreString(score), Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light), }, @@ -496,7 +496,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(2f, 0f), @@ -704,7 +704,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards Child = new OsuSpriteText { - Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0), + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true), diff --git a/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs b/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs index e2c841f88a..ac7e3856ac 100644 --- a/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs +++ b/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.SelectV2 Content.Anchor = Anchor.Centre; Content.Origin = Anchor.Centre; - Content.Shear = new Vector2(OsuGame.SHEAR, 0); + Content.Shear = OsuGame.SHEAR; Content.AddRange(new Drawable[] { @@ -87,7 +87,7 @@ namespace osu.Game.Screens.SelectV2 AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(4), - Shear = new Vector2(-OsuGame.SHEAR, 0), + Shear = -OsuGame.SHEAR, Children = new Drawable[] { new Container From 0aff50fbf5eb4284823f3dfcdc92b34601a9ba11 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:23:14 +0900 Subject: [PATCH 50/58] Rename song select v2 classes and namespaces This aims to bring some conformity to naming to make it easier to understand component structure for new components. Renames are pulled out of the song select v2 changes and are more relevant there due to many new classes being added. - `V2` suffix is dropped, with v2 components being moved to a relevant V2 namespace. - Related classes have a prefix of the area they are used. - Experimenting with using partial/nested classes in the song select v2 implementation. Not committing to this yet but want to see how it plays out. - Moved base carousel components to a generic namespace to avoid confusion with actual beatmap carousel implementation. --- .../DailyChallenge/TestSceneDailyChallenge.cs | 4 +- .../BeatmapCarouselTestScene.cs} | 9 +- .../TestSceneBeatmapCarousel.cs} | 4 +- ...TestSceneBeatmapCarouselArtistGrouping.cs} | 4 +- ...SceneBeatmapCarouselDifficultyGrouping.cs} | 5 +- .../TestSceneBeatmapCarouselNoGrouping.cs} | 5 +- .../TestSceneBeatmapCarouselScrolling.cs} | 4 +- .../TestSceneFooterButtonMods.cs} | 14 +- .../SongSelectV2/TestSceneLeaderboardScore.cs | 10 +- ...cultyPanel.cs => TestScenePanelBeatmap.cs} | 5 +- ....cs => TestScenePanelBeatmapStandalone.cs} | 5 +- ...V2GroupPanel.cs => TestScenePanelGroup.cs} | 5 +- ...uselV2SetPanel.cs => TestScenePanelSet.cs} | 5 +- ...s => TestScenePanelUpdateBeatmapButton.cs} | 6 +- .../TestSceneScreenFooter.cs | 10 +- .../SongSelectV2/TestSceneSongSelect.cs | 6 +- .../Carousel}/Carousel.cs | 2 +- .../Carousel}/CarouselItem.cs | 2 +- .../Carousel}/ICarouselFilter.cs | 2 +- .../Carousel}/ICarouselPanel.cs | 2 +- .../DailyChallengeLeaderboard.cs | 10 +- osu.Game/Screens/SelectV2/BeatmapCarousel.cs | 1 + .../SelectV2/BeatmapCarouselFilterGrouping.cs | 1 + .../SelectV2/BeatmapCarouselFilterSorting.cs | 1 + ...dScoreV2.cs => BeatmapLeaderboardScore.cs} | 6 +- .../SelectV2/Footer/BeatmapOptionsPopover.cs | 195 ----------------- ...ooterButtonMods.cs => FooterButtonMods.cs} | 6 +- ...uttonOptions.cs => FooterButtonOptions.cs} | 7 +- .../SelectV2/FooterButtonOptions_Popover.cs | 198 ++++++++++++++++++ ...rButtonRandom.cs => FooterButtonRandom.cs} | 4 +- .../SelectV2/{PanelBase.cs => Panel.cs} | 3 +- osu.Game/Screens/SelectV2/PanelBeatmap.cs | 11 +- osu.Game/Screens/SelectV2/PanelBeatmapSet.cs | 11 +- .../SelectV2/PanelBeatmapStandalone.cs | 15 +- osu.Game/Screens/SelectV2/PanelGroup.cs | 3 +- .../SelectV2/PanelGroupStarDifficulty.cs | 2 +- ...pLocalRank.cs => PanelLocalRankDisplay.cs} | 4 +- ...nelBackground.cs => PanelSetBackground.cs} | 2 +- ...tButton.cs => PanelUpdateBeatmapButton.cs} | 4 +- osu.Game/Screens/SelectV2/SongSelect.cs | 7 +- 40 files changed, 308 insertions(+), 292 deletions(-) rename osu.Game.Tests/Visual/{SongSelect/BeatmapCarouselV2TestScene.cs => SongSelectV2/BeatmapCarouselTestScene.cs} (97%) rename osu.Game.Tests/Visual/{SongSelect/TestSceneBeatmapCarouselV2.cs => SongSelectV2/TestSceneBeatmapCarousel.cs} (95%) rename osu.Game.Tests/Visual/{SongSelect/TestSceneBeatmapCarouselV2ArtistGrouping.cs => SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs} (98%) rename osu.Game.Tests/Visual/{SongSelect/TestSceneBeatmapCarouselV2DifficultyGrouping.cs => SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs} (97%) rename osu.Game.Tests/Visual/{SongSelect/TestSceneBeatmapCarouselV2NoGrouping.cs => SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs} (97%) rename osu.Game.Tests/Visual/{SongSelect/TestSceneBeatmapCarouselV2Scrolling.cs => SongSelectV2/TestSceneBeatmapCarouselScrolling.cs} (94%) rename osu.Game.Tests/Visual/{UserInterface/TestSceneScreenFooterButtonMods.cs => SongSelectV2/TestSceneFooterButtonMods.cs} (92%) rename osu.Game.Tests/Visual/SongSelectV2/{TestSceneBeatmapCarouselV2DifficultyPanel.cs => TestScenePanelBeatmap.cs} (95%) rename osu.Game.Tests/Visual/SongSelectV2/{TestSceneBeatmapCarouselV2StandalonePanel.cs => TestScenePanelBeatmapStandalone.cs} (95%) rename osu.Game.Tests/Visual/SongSelectV2/{TestSceneBeatmapCarouselV2GroupPanel.cs => TestScenePanelGroup.cs} (96%) rename osu.Game.Tests/Visual/SongSelectV2/{TestSceneBeatmapCarouselV2SetPanel.cs => TestScenePanelSet.cs} (95%) rename osu.Game.Tests/Visual/SongSelectV2/{TestSceneUpdateBeatmapSetButtonV2.cs => TestScenePanelUpdateBeatmapButton.cs} (90%) rename osu.Game.Tests/Visual/{UserInterface => SongSelectV2}/TestSceneScreenFooter.cs (98%) rename osu.Game/{Screens/SelectV2 => Graphics/Carousel}/Carousel.cs (99%) rename osu.Game/{Screens/SelectV2 => Graphics/Carousel}/CarouselItem.cs (98%) rename osu.Game/{Screens/SelectV2 => Graphics/Carousel}/ICarouselFilter.cs (95%) rename osu.Game/{Screens/SelectV2 => Graphics/Carousel}/ICarouselPanel.cs (97%) rename osu.Game/Screens/SelectV2/{Leaderboards/LeaderboardScoreV2.cs => BeatmapLeaderboardScore.cs} (99%) delete mode 100644 osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs rename osu.Game/Screens/SelectV2/{Footer/ScreenFooterButtonMods.cs => FooterButtonMods.cs} (98%) rename osu.Game/Screens/SelectV2/{Footer/ScreenFooterButtonOptions.cs => FooterButtonOptions.cs} (76%) create mode 100644 osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs rename osu.Game/Screens/SelectV2/{Footer/ScreenFooterButtonRandom.cs => FooterButtonRandom.cs} (97%) rename osu.Game/Screens/SelectV2/{PanelBase.cs => Panel.cs} (98%) rename osu.Game/Screens/SelectV2/{TopLocalRank.cs => PanelLocalRankDisplay.cs} (96%) rename osu.Game/Screens/SelectV2/{BeatmapSetPanelBackground.cs => PanelSetBackground.cs} (97%) rename osu.Game/Screens/SelectV2/{UpdateBeatmapSetButton.cs => PanelUpdateBeatmapButton.cs} (98%) diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index 185ebc1d39..f1422b4654 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -15,7 +15,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.SelectV2.Leaderboards; +using osu.Game.Screens.SelectV2; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual.Metadata; using osu.Game.Tests.Visual.OnlinePlay; @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge AddStep("force transforms to finish", () => FinishTransforms(true)); AddStep("right click second score", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1)); + InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(1)); InputManager.Click(MouseButton.Right); }); AddAssert("use these mods not present", diff --git a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs similarity index 97% rename from osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs rename to osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs index f2faeab1c4..28a0948696 100644 --- a/osu.Game.Tests/Visual/SongSelect/BeatmapCarouselV2TestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselTestScene.cs @@ -16,6 +16,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osu.Game.Screens.Select; @@ -27,9 +28,9 @@ using osuTK.Graphics; using osuTK.Input; using BeatmapCarousel = osu.Game.Screens.SelectV2.BeatmapCarousel; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { - public abstract partial class BeatmapCarouselV2TestScene : OsuManualInputManagerTestScene + public abstract partial class BeatmapCarouselTestScene : OsuManualInputManagerTestScene { protected readonly BindableList BeatmapSets = new BindableList(); @@ -47,7 +48,7 @@ namespace osu.Game.Tests.Visual.SongSelect private int beatmapCount; - protected BeatmapCarouselV2TestScene() + protected BeatmapCarouselTestScene() { store = new TestBeatmapStore { @@ -191,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect .Where(p => ((ICarouselPanel)p).Item?.IsVisible == true) .OrderBy(p => p.Y) .ElementAt(index) - .ChildrenOfType().Single() + .ChildrenOfType().Single() .TriggerClick(); }); } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs similarity index 95% rename from osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs index 30ca26ce68..5fd921645b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs @@ -10,13 +10,13 @@ using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Tests.Resources; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { /// /// Covers common steps which can be used for manual testing. /// [TestFixture] - public partial class TestSceneBeatmapCarouselV2 : BeatmapCarouselV2TestScene + public partial class TestSceneBeatmapCarousel : BeatmapCarouselTestScene { [Test] [Explicit] diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2ArtistGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs similarity index 98% rename from osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2ArtistGrouping.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs index c378871eac..f0caa796b6 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2ArtistGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs @@ -9,10 +9,10 @@ using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Screens.SelectV2; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { [TestFixture] - public partial class TestSceneBeatmapCarouselV2ArtistGrouping : BeatmapCarouselV2TestScene + public partial class TestSceneBeatmapCarouselArtistGrouping : BeatmapCarouselTestScene { [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2DifficultyGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs similarity index 97% rename from osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2DifficultyGrouping.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs index 239c693ee1..a4cdf8abcb 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2DifficultyGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs @@ -5,15 +5,16 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Screens.SelectV2; using osuTK; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { [TestFixture] - public partial class TestSceneBeatmapCarouselV2DifficultyGrouping : BeatmapCarouselV2TestScene + public partial class TestSceneBeatmapCarouselDifficultyGrouping : BeatmapCarouselTestScene { [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2NoGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs similarity index 97% rename from osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2NoGrouping.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs index b4048a5355..ac02d7a3a9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2NoGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs @@ -5,16 +5,17 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Screens.SelectV2; using osuTK; using osuTK.Input; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { [TestFixture] - public partial class TestSceneBeatmapCarouselV2NoGrouping : BeatmapCarouselV2TestScene + public partial class TestSceneBeatmapCarouselNoGrouping : BeatmapCarouselTestScene { [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2Scrolling.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs similarity index 94% rename from osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2Scrolling.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs index 890e1dd6e3..da3fc98c19 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselV2Scrolling.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselScrolling.cs @@ -8,10 +8,10 @@ using osu.Framework.Testing; using osu.Game.Screens.Select; using osu.Game.Screens.SelectV2; -namespace osu.Game.Tests.Visual.SongSelect +namespace osu.Game.Tests.Visual.SongSelectV2 { [TestFixture] - public partial class TestSceneBeatmapCarouselV2Scrolling : BeatmapCarouselV2TestScene + public partial class TestSceneBeatmapCarouselScrolling : BeatmapCarouselTestScene { [SetUpSteps] public void SetUpSteps() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneFooterButtonMods.cs similarity index 92% rename from osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneFooterButtonMods.cs index e86f83ee15..5c2c6eaf1d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneFooterButtonMods.cs @@ -13,19 +13,19 @@ using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.SelectV2.Footer; +using osu.Game.Screens.SelectV2; using osu.Game.Utils; -namespace osu.Game.Tests.Visual.UserInterface +namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneScreenFooterButtonMods : OsuTestScene + public partial class TestSceneFooterButtonMods : OsuTestScene { private readonly TestScreenFooterButtonMods footerButtonMods; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); - public TestSceneScreenFooterButtonMods() + public TestSceneFooterButtonMods() { Add(footerButtonMods = new TestScreenFooterButtonMods(new TestModSelectOverlay()) { @@ -98,9 +98,9 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestUnrankedBadge() { AddStep(@"Add unranked mod", () => changeMods(new[] { new OsuModDeflate() })); - AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 1); + AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 1); AddStep(@"Clear selected mod", () => changeMods(Array.Empty())); - AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 0); + AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType().Single().Alpha == 0); } private void changeMods(IReadOnlyList mods) => footerButtonMods.Current.Value = mods; @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.UserInterface } } - private partial class TestScreenFooterButtonMods : ScreenFooterButtonMods + private partial class TestScreenFooterButtonMods : FooterButtonMods { public new OsuSpriteText MultiplierText => base.MultiplierText; diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs index 08c0c92285..b59a31c173 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneLeaderboardScore.cs @@ -20,7 +20,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Screens.SelectV2.Leaderboards; +using osu.Game.Screens.SelectV2; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 foreach (var scoreInfo in getTestScores()) { - fillFlow.Add(new LeaderboardScoreV2(scoreInfo) + fillFlow.Add(new BeatmapLeaderboardScore(scoreInfo) { Rank = scoreInfo.Position, IsPersonalBest = scoreInfo.User.Id == 2, @@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 foreach (var scoreInfo in getTestScores()) { - fillFlow.Add(new LeaderboardScoreV2(scoreInfo) + fillFlow.Add(new BeatmapLeaderboardScore(scoreInfo) { Rank = scoreInfo.Position, IsPersonalBest = scoreInfo.User.Id == 2, @@ -108,7 +108,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Test] public void TestUseTheseModsDoesNotCopySystemMods() { - LeaderboardScoreV2 score = null!; + BeatmapLeaderboardScore score = null!; AddStep("create content", () => { @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 Date = DateTimeOffset.Now.AddYears(-2), }; - fillFlow.Add(score = new LeaderboardScoreV2(scoreInfo) + fillFlow.Add(score = new BeatmapLeaderboardScore(scoreInfo) { Rank = scoreInfo.Position, Shear = Vector2.Zero, diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2DifficultyPanel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmap.cs similarity index 95% rename from osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2DifficultyPanel.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmap.cs index 1947721d5d..53a1355fc2 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2DifficultyPanel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmap.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Overlays; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; @@ -18,14 +19,14 @@ using osuTK; namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneBeatmapCarouselV2DifficultyPanel : ThemeComparisonTestScene + public partial class TestScenePanelBeatmap : ThemeComparisonTestScene { [Resolved] private BeatmapManager beatmaps { get; set; } = null!; private BeatmapInfo beatmap = null!; - public TestSceneBeatmapCarouselV2DifficultyPanel() + public TestScenePanelBeatmap() : base(false) { } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2StandalonePanel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmapStandalone.cs similarity index 95% rename from osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2StandalonePanel.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmapStandalone.cs index 2dbe9e6cd1..4adee17868 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2StandalonePanel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelBeatmapStandalone.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Overlays; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; @@ -18,14 +19,14 @@ using osuTK; namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneBeatmapCarouselV2StandalonePanel : ThemeComparisonTestScene + public partial class TestScenePanelBeatmapStandalone : ThemeComparisonTestScene { [Resolved] private BeatmapManager beatmaps { get; set; } = null!; private BeatmapInfo beatmap = null!; - public TestSceneBeatmapCarouselV2StandalonePanel() + public TestScenePanelBeatmapStandalone() : base(false) { } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelGroup.cs similarity index 96% rename from osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestScenePanelGroup.cs index d62aee77f3..54c6cb1c0e 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2GroupPanel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelGroup.cs @@ -6,15 +6,16 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; +using osu.Game.Graphics.Carousel; using osu.Game.Screens.SelectV2; using osu.Game.Tests.Visual.UserInterface; using osuTK; namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneBeatmapCarouselV2GroupPanel : ThemeComparisonTestScene + public partial class TestScenePanelGroup : ThemeComparisonTestScene { - public TestSceneBeatmapCarouselV2GroupPanel() + public TestScenePanelGroup() : base(false) { } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2SetPanel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelSet.cs similarity index 95% rename from osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2SetPanel.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestScenePanelSet.cs index ef34394e12..16f6b2cc9c 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselV2SetPanel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelSet.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Overlays; using osu.Game.Screens.SelectV2; using osu.Game.Tests.Resources; @@ -16,14 +17,14 @@ using osuTK; namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneBeatmapCarouselV2SetPanel : ThemeComparisonTestScene + public partial class TestScenePanelSet : ThemeComparisonTestScene { [Resolved] private BeatmapManager beatmaps { get; set; } = null!; private BeatmapSetInfo beatmapSet = null!; - public TestSceneBeatmapCarouselV2SetPanel() + public TestScenePanelSet() : base(false) { } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneUpdateBeatmapSetButtonV2.cs b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelUpdateBeatmapButton.cs similarity index 90% rename from osu.Game.Tests/Visual/SongSelectV2/TestSceneUpdateBeatmapSetButtonV2.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestScenePanelUpdateBeatmapButton.cs index ba3f2635b0..781691d3db 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneUpdateBeatmapSetButtonV2.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestScenePanelUpdateBeatmapButton.cs @@ -9,14 +9,14 @@ using osu.Game.Screens.SelectV2; namespace osu.Game.Tests.Visual.SongSelectV2 { - public partial class TestSceneUpdateBeatmapSetButtonV2 : OsuTestScene + public partial class TestScenePanelUpdateBeatmapButton : OsuTestScene { - private UpdateBeatmapSetButton button = null!; + private PanelUpdateBeatmapButton button = null!; [SetUp] public void SetUp() => Schedule(() => { - Child = button = new UpdateBeatmapSetButton + Child = button = new PanelUpdateBeatmapButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneScreenFooter.cs similarity index 98% rename from osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs rename to osu.Game.Tests/Visual/SongSelectV2/TestSceneScreenFooter.cs index 054bbb39d1..bdecebd64f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneScreenFooter.cs @@ -15,9 +15,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Screens.Footer; -using osu.Game.Screens.SelectV2.Footer; +using osu.Game.Screens.SelectV2; -namespace osu.Game.Tests.Visual.UserInterface +namespace osu.Game.Tests.Visual.SongSelectV2 { public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene { @@ -51,9 +51,9 @@ namespace osu.Game.Tests.Visual.UserInterface screenFooter.SetButtons(new ScreenFooterButton[] { - new ScreenFooterButtonMods(modOverlay) { Current = SelectedMods }, - new ScreenFooterButtonRandom(), - new ScreenFooterButtonOptions(), + new FooterButtonMods(modOverlay) { Current = SelectedMods }, + new FooterButtonRandom(), + new FooterButtonOptions(), }); }); diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs index 630f3c95ee..986ad6fc46 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneSongSelect.cs @@ -22,7 +22,7 @@ using osu.Game.Rulesets.Taiko; using osu.Game.Screens; using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; -using osu.Game.Screens.SelectV2.Footer; +using osu.Game.Screens.SelectV2; using osuTK.Input; namespace osu.Game.Tests.Visual.SongSelectV2 @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { base.SetUpSteps(); - AddStep("load screen", () => Stack.Push(new Screens.SelectV2.SoloSongSelect())); + AddStep("load screen", () => Stack.Push(new SoloSongSelect())); AddUntilStep("wait for load", () => Stack.CurrentScreen is Screens.SelectV2.SongSelect songSelect && songSelect.IsLoaded); } @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { AddStep("Press F1", () => { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); AddAssert("Overlay visible", () => this.ChildrenOfType().Single().State.Value == Visibility.Visible); diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Graphics/Carousel/Carousel.cs similarity index 99% rename from osu.Game/Screens/SelectV2/Carousel.cs rename to osu.Game/Graphics/Carousel/Carousel.cs index 7b1fd6e999..a9c8aecd6c 100644 --- a/osu.Game/Screens/SelectV2/Carousel.cs +++ b/osu.Game/Graphics/Carousel/Carousel.cs @@ -24,7 +24,7 @@ using osu.Game.Input.Bindings; using osuTK; using osuTK.Input; -namespace osu.Game.Screens.SelectV2 +namespace osu.Game.Graphics.Carousel { /// /// A highly efficient vertical list display that is used primarily for the song select screen, diff --git a/osu.Game/Screens/SelectV2/CarouselItem.cs b/osu.Game/Graphics/Carousel/CarouselItem.cs similarity index 98% rename from osu.Game/Screens/SelectV2/CarouselItem.cs rename to osu.Game/Graphics/Carousel/CarouselItem.cs index 36dc48a497..223c8d9869 100644 --- a/osu.Game/Screens/SelectV2/CarouselItem.cs +++ b/osu.Game/Graphics/Carousel/CarouselItem.cs @@ -3,7 +3,7 @@ using System; -namespace osu.Game.Screens.SelectV2 +namespace osu.Game.Graphics.Carousel { /// /// Represents a single display item for display in a . diff --git a/osu.Game/Screens/SelectV2/ICarouselFilter.cs b/osu.Game/Graphics/Carousel/ICarouselFilter.cs similarity index 95% rename from osu.Game/Screens/SelectV2/ICarouselFilter.cs rename to osu.Game/Graphics/Carousel/ICarouselFilter.cs index f510a7cd4b..570f480aab 100644 --- a/osu.Game/Screens/SelectV2/ICarouselFilter.cs +++ b/osu.Game/Graphics/Carousel/ICarouselFilter.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace osu.Game.Screens.SelectV2 +namespace osu.Game.Graphics.Carousel { /// /// An interface representing a filter operation which can be run on a . diff --git a/osu.Game/Screens/SelectV2/ICarouselPanel.cs b/osu.Game/Graphics/Carousel/ICarouselPanel.cs similarity index 97% rename from osu.Game/Screens/SelectV2/ICarouselPanel.cs rename to osu.Game/Graphics/Carousel/ICarouselPanel.cs index 4fba0d2827..5f0ebc263c 100644 --- a/osu.Game/Screens/SelectV2/ICarouselPanel.cs +++ b/osu.Game/Graphics/Carousel/ICarouselPanel.cs @@ -5,7 +5,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; -namespace osu.Game.Screens.SelectV2 +namespace osu.Game.Graphics.Carousel { /// /// An interface to be attached to any s which are used for display inside a . diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs index 4736ba28db..401053599e 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs @@ -17,7 +17,7 @@ using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; -using osu.Game.Screens.SelectV2.Leaderboards; +using osu.Game.Screens.SelectV2; using osuTK; namespace osu.Game.Screens.OnlinePlay.DailyChallenge @@ -40,7 +40,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge private readonly Room room; private readonly PlaylistItem playlistItem; - private FillFlowContainer scoreFlow = null!; + private FillFlowContainer scoreFlow = null!; private Container userBestContainer = null!; private SectionHeader userBestHeader = null!; private LoadingLayer loadingLayer = null!; @@ -91,7 +91,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge new OsuScrollContainer { RelativeSizeAxes = Axes.Both, - Child = scoreFlow = new FillFlowContainer + Child = scoreFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -158,7 +158,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge } else { - LoadComponentsAsync(best.Select((s, index) => new LeaderboardScoreV2(s, sheared: false) + LoadComponentsAsync(best.Select((s, index) => new BeatmapLeaderboardScore(s, sheared: false) { Rank = index + 1, IsPersonalBest = s.UserID == api.LocalUser.Value.Id, @@ -178,7 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge if (userBest != null) { - userBestContainer.Add(new LeaderboardScoreV2(userBest, sheared: false) + userBestContainer.Add(new BeatmapLeaderboardScore(userBest, sheared: false) { Rank = userBest.Position, IsPersonalBest = true, diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs index 994b0fb6c0..9cb7d152de 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Pooling; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select; diff --git a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs index 8f9d5cc31b..3360437544 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; diff --git a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterSorting.cs b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterSorting.cs index 3cdbbb4fed..22a67321db 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterSorting.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterSorting.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Game.Beatmaps; +using osu.Game.Graphics.Carousel; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Utils; diff --git a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs similarity index 99% rename from osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs rename to osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs index 0b7b2ebbc1..c9413a9414 100644 --- a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs +++ b/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs @@ -41,9 +41,9 @@ using osuTK; using osuTK.Graphics; using CommonStrings = osu.Game.Localisation.CommonStrings; -namespace osu.Game.Screens.SelectV2.Leaderboards +namespace osu.Game.Screens.SelectV2 { - public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip + public partial class BeatmapLeaderboardScore : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip { public Bindable> SelectedMods = new Bindable>(); @@ -117,7 +117,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards public ITooltip GetCustomTooltip() => new LeaderboardScoreTooltip(); public virtual ScoreInfo TooltipContent => score; - public LeaderboardScoreV2(ScoreInfo score, bool sheared = true) + public BeatmapLeaderboardScore(ScoreInfo score, bool sheared = true) { this.score = score; this.sheared = sheared; diff --git a/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs b/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs deleted file mode 100644 index fb2e32dfdc..0000000000 --- a/osu.Game/Screens/SelectV2/Footer/BeatmapOptionsPopover.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -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.Framework.Input.Events; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Collections; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Localisation; -using osu.Game.Overlays; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; -using WebCommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; - -namespace osu.Game.Screens.SelectV2.Footer -{ - public partial class BeatmapOptionsPopover : OsuPopover - { - private FillFlowContainer buttonFlow = null!; - private readonly ScreenFooterButtonOptions footerButton; - - [Cached] - private readonly OverlayColourProvider colourProvider; - - private WorkingBeatmap beatmapWhenOpening = null!; - - [Resolved] - private IBindable beatmap { get; set; } = null!; - - public BeatmapOptionsPopover(ScreenFooterButtonOptions footerButton, OverlayColourProvider colourProvider) - { - this.footerButton = footerButton; - this.colourProvider = colourProvider; - } - - [BackgroundDependencyLoader] - private void load(ManageCollectionsDialog? manageCollectionsDialog, OsuColour colours, BeatmapManager? beatmapManager) - { - Content.Padding = new MarginPadding(5); - - Child = buttonFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(3), - }; - - beatmapWhenOpening = beatmap.Value; - - addHeader(CommonStrings.General); - addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show()); - - addHeader(SongSelectStrings.ForAllDifficulties, beatmapWhenOpening.BeatmapSetInfo.ToString()); - addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => { }, colours.Red1); // songSelect?.DeleteBeatmap(beatmapWhenOpening.BeatmapSetInfo); - - addHeader(SongSelectStrings.ForSelectedDifficulty, beatmapWhenOpening.BeatmapInfo.DifficultyName); - // TODO: make work, and make show "unplayed" or "played" based on status. - addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, null); - addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => { }, colours.Red1); // songSelect?.ClearScores(beatmapWhenOpening.BeatmapInfo); - - // if (songSelect != null && songSelect.AllowEditing) - addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => { }); // songSelect.Edit(beatmapWhenOpening.BeatmapInfo); - - addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => beatmapManager?.Hide(beatmapWhenOpening.BeatmapInfo)); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - ScheduleAfterChildren(() => GetContainingFocusManager()!.ChangeFocus(this)); - - beatmap.BindValueChanged(_ => Hide()); - } - - private void addHeader(LocalisableString text, string? context = null) - { - var textFlow = new OsuTextFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Padding = new MarginPadding(10), - }; - - textFlow.AddText(text, t => t.Font = OsuFont.Default.With(weight: FontWeight.SemiBold)); - - if (context != null) - { - textFlow.NewLine(); - textFlow.AddText(context, t => - { - t.Colour = colourProvider.Content2; - t.Font = t.Font.With(size: 13); - }); - } - - buttonFlow.Add(textFlow); - } - - private void addButton(LocalisableString text, IconUsage icon, Action? action, Color4? colour = null) - { - var button = new OptionButton - { - Text = text, - Icon = icon, - TextColour = colour, - Action = () => - { - Scheduler.AddDelayed(Hide, 50); - action?.Invoke(); - }, - }; - - buttonFlow.Add(button); - } - - private partial class OptionButton : OsuButton - { - public IconUsage Icon { get; init; } - public Color4? TextColour { get; init; } - - public OptionButton() - { - Size = new Vector2(265, 50); - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - BackgroundColour = colourProvider.Background3; - - SpriteText.Colour = TextColour ?? Color4.White; - Content.CornerRadius = 10; - - Add(new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(17), - X = 15, - Icon = Icon, - Colour = TextColour ?? Color4.White, - }); - } - - protected override SpriteText CreateText() => new OsuSpriteText - { - Depth = -1, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - X = 40 - }; - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - // don't absorb control as ToolbarRulesetSelector uses control + number to navigate - if (e.ControlPressed) return false; - - if (!e.Repeat && e.Key >= Key.Number1 && e.Key <= Key.Number9) - { - int requested = e.Key - Key.Number1; - - OptionButton? found = buttonFlow.Children.OfType().ElementAtOrDefault(requested); - - if (found != null) - { - found.TriggerClick(); - return true; - } - } - - return base.OnKeyDown(e); - } - - protected override void UpdateState(ValueChangedEvent state) - { - base.UpdateState(state); - footerButton.OverlayState.Value = state.NewValue; - } - } -} diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs b/osu.Game/Screens/SelectV2/FooterButtonMods.cs similarity index 98% rename from osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs rename to osu.Game/Screens/SelectV2/FooterButtonMods.cs index 61d69ae197..833ea96139 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonMods.cs @@ -28,9 +28,9 @@ using osu.Game.Utils; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.SelectV2.Footer +namespace osu.Game.Screens.SelectV2 { - public partial class ScreenFooterButtonMods : ScreenFooterButton, IHasCurrentValue> + public partial class FooterButtonMods : ScreenFooterButton, IHasCurrentValue> { private const float bar_height = 30f; private const float mod_display_portion = 0.65f; @@ -58,7 +58,7 @@ namespace osu.Game.Screens.SelectV2.Footer [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public ScreenFooterButtonMods(ModSelectOverlay overlay) + public FooterButtonMods(ModSelectOverlay overlay) : base(overlay) { } diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs b/osu.Game/Screens/SelectV2/FooterButtonOptions.cs similarity index 76% rename from osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs rename to osu.Game/Screens/SelectV2/FooterButtonOptions.cs index 72409b5566..41edaf2a02 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonOptions.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonOptions.cs @@ -5,15 +5,14 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Screens.Footer; -namespace osu.Game.Screens.SelectV2.Footer +namespace osu.Game.Screens.SelectV2 { - public partial class ScreenFooterButtonOptions : ScreenFooterButton, IHasPopover + public partial class FooterButtonOptions : ScreenFooterButton, IHasPopover { [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -29,6 +28,6 @@ namespace osu.Game.Screens.SelectV2.Footer Action = this.ShowPopover; } - public Popover GetPopover() => new BeatmapOptionsPopover(this, colourProvider); + public Framework.Graphics.UserInterface.Popover GetPopover() => new Popover(this, colourProvider); } } diff --git a/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs b/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs new file mode 100644 index 0000000000..76b841ee99 --- /dev/null +++ b/osu.Game/Screens/SelectV2/FooterButtonOptions_Popover.cs @@ -0,0 +1,198 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +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.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Collections; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; +using WebCommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; + +namespace osu.Game.Screens.SelectV2 +{ + public partial class FooterButtonOptions + { + public partial class Popover : OsuPopover + { + private FillFlowContainer buttonFlow = null!; + private readonly FooterButtonOptions footerButton; + + [Cached] + private readonly OverlayColourProvider colourProvider; + + private WorkingBeatmap beatmapWhenOpening = null!; + + [Resolved] + private IBindable beatmap { get; set; } = null!; + + public Popover(FooterButtonOptions footerButton, OverlayColourProvider colourProvider) + { + this.footerButton = footerButton; + this.colourProvider = colourProvider; + } + + [BackgroundDependencyLoader] + private void load(ManageCollectionsDialog? manageCollectionsDialog, OsuColour colours, BeatmapManager? beatmapManager) + { + Content.Padding = new MarginPadding(5); + + Child = buttonFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(3), + }; + + beatmapWhenOpening = beatmap.Value; + + addHeader(CommonStrings.General); + addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show()); + + addHeader(SongSelectStrings.ForAllDifficulties, beatmapWhenOpening.BeatmapSetInfo.ToString()); + addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => { }, colours.Red1); // songSelect?.DeleteBeatmap(beatmapWhenOpening.BeatmapSetInfo); + + addHeader(SongSelectStrings.ForSelectedDifficulty, beatmapWhenOpening.BeatmapInfo.DifficultyName); + // TODO: make work, and make show "unplayed" or "played" based on status. + addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, null); + addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => { }, colours.Red1); // songSelect?.ClearScores(beatmapWhenOpening.BeatmapInfo); + + // if (songSelect != null && songSelect.AllowEditing) + addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => { }); // songSelect.Edit(beatmapWhenOpening.BeatmapInfo); + + addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => beatmapManager?.Hide(beatmapWhenOpening.BeatmapInfo)); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ScheduleAfterChildren(() => GetContainingFocusManager()!.ChangeFocus(this)); + + beatmap.BindValueChanged(_ => Hide()); + } + + private void addHeader(LocalisableString text, string? context = null) + { + var textFlow = new OsuTextFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding(10), + }; + + textFlow.AddText(text, t => t.Font = OsuFont.Default.With(weight: FontWeight.SemiBold)); + + if (context != null) + { + textFlow.NewLine(); + textFlow.AddText(context, t => + { + t.Colour = colourProvider.Content2; + t.Font = t.Font.With(size: 13); + }); + } + + buttonFlow.Add(textFlow); + } + + private void addButton(LocalisableString text, IconUsage icon, Action? action, Color4? colour = null) + { + var button = new OptionButton + { + Text = text, + Icon = icon, + TextColour = colour, + Action = () => + { + Scheduler.AddDelayed(Hide, 50); + action?.Invoke(); + }, + }; + + buttonFlow.Add(button); + } + + private partial class OptionButton : OsuButton + { + public IconUsage Icon { get; init; } + public Color4? TextColour { get; init; } + + public OptionButton() + { + Size = new Vector2(265, 50); + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundColour = colourProvider.Background3; + + SpriteText.Colour = TextColour ?? Color4.White; + Content.CornerRadius = 10; + + Add(new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(17), + X = 15, + Icon = Icon, + Colour = TextColour ?? Color4.White, + }); + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + X = 40 + }; + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + // don't absorb control as ToolbarRulesetSelector uses control + number to navigate + if (e.ControlPressed) return false; + + if (!e.Repeat && e.Key >= Key.Number1 && e.Key <= Key.Number9) + { + int requested = e.Key - Key.Number1; + + OptionButton? found = buttonFlow.Children.OfType().ElementAtOrDefault(requested); + + if (found != null) + { + found.TriggerClick(); + return true; + } + } + + return base.OnKeyDown(e); + } + + protected override void UpdateState(ValueChangedEvent state) + { + base.UpdateState(state); + footerButton.OverlayState.Value = state.NewValue; + } + } + } +} diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs b/osu.Game/Screens/SelectV2/FooterButtonRandom.cs similarity index 97% rename from osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs rename to osu.Game/Screens/SelectV2/FooterButtonRandom.cs index dbdb6fe79b..88b139da97 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonRandom.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonRandom.cs @@ -14,9 +14,9 @@ using osu.Game.Screens.Footer; using osuTK; using osuTK.Input; -namespace osu.Game.Screens.SelectV2.Footer +namespace osu.Game.Screens.SelectV2 { - public partial class ScreenFooterButtonRandom : ScreenFooterButton + public partial class FooterButtonRandom : ScreenFooterButton { public Action? NextRandom { get; set; } public Action? PreviousRandom { get; set; } diff --git a/osu.Game/Screens/SelectV2/PanelBase.cs b/osu.Game/Screens/SelectV2/Panel.cs similarity index 98% rename from osu.Game/Screens/SelectV2/PanelBase.cs rename to osu.Game/Screens/SelectV2/Panel.cs index 32da02a189..c22a88a55f 100644 --- a/osu.Game/Screens/SelectV2/PanelBase.cs +++ b/osu.Game/Screens/SelectV2/Panel.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osuTK; @@ -19,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { - public abstract partial class PanelBase : PoolableDrawable, ICarouselPanel + public abstract partial class Panel : PoolableDrawable, ICarouselPanel { private const float corner_radius = 10; diff --git a/osu.Game/Screens/SelectV2/PanelBeatmap.cs b/osu.Game/Screens/SelectV2/PanelBeatmap.cs index d4bf3519fa..6742577389 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmap.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmap.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -22,7 +23,7 @@ using osuTK; namespace osu.Game.Screens.SelectV2 { - public partial class PanelBeatmap : PanelBase + public partial class PanelBeatmap : Panel { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT; @@ -30,7 +31,7 @@ namespace osu.Game.Screens.SelectV2 private ConstrainedIconContainer difficultyIcon = null!; private OsuSpriteText keyCountText = null!; private StarRatingDisplay starRatingDisplay = null!; - private TopLocalRank difficultyRank = null!; + private PanelLocalRankDisplay localRank = null!; private OsuSpriteText difficultyText = null!; private OsuSpriteText authorText = null!; @@ -100,7 +101,7 @@ namespace osu.Game.Screens.SelectV2 Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - difficultyRank = new TopLocalRank + localRank = new PanelLocalRankDisplay { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -174,7 +175,7 @@ namespace osu.Game.Screens.SelectV2 difficultyIcon.Icon = beatmap.Ruleset.CreateInstance().CreateIcon(); - difficultyRank.Beatmap = beatmap; + localRank.Beatmap = beatmap; difficultyText.Text = beatmap.DifficultyName; authorText.Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmap.Metadata.Author.Username); @@ -186,7 +187,7 @@ namespace osu.Game.Screens.SelectV2 { base.FreeAfterUse(); - difficultyRank.Beatmap = null; + localRank.Beatmap = null; starDifficultyBindable = null; } diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs b/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs index 9e9ef612ea..179d4d6444 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapSet.cs @@ -11,22 +11,23 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; namespace osu.Game.Screens.SelectV2 { - public partial class PanelBeatmapSet : PanelBase + public partial class PanelBeatmapSet : Panel { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f; - private BeatmapSetPanelBackground background = null!; + private PanelSetBackground background = null!; private OsuSpriteText titleText = null!; private OsuSpriteText artistText = null!; private Drawable chevronIcon = null!; - private UpdateBeatmapSetButton updateButton = null!; + private PanelUpdateBeatmapButton updateButton = null!; private BeatmapSetOnlineStatusPill statusPill = null!; private DifficultySpectrumDisplay difficultiesDisplay = null!; @@ -60,7 +61,7 @@ namespace osu.Game.Screens.SelectV2 }, }; - Background = background = new BeatmapSetPanelBackground + Background = background = new PanelSetBackground { RelativeSizeAxes = Axes.Both, }; @@ -89,7 +90,7 @@ namespace osu.Game.Screens.SelectV2 Margin = new MarginPadding { Top = 5f }, Children = new Drawable[] { - updateButton = new UpdateBeatmapSetButton + updateButton = new PanelUpdateBeatmapButton { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs index f893bb0caf..a0d7484587 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs @@ -13,6 +13,7 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; @@ -23,7 +24,7 @@ using osuTK; namespace osu.Game.Screens.SelectV2 { - public partial class PanelBeatmapStandalone : PanelBase + public partial class PanelBeatmapStandalone : Panel { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f; @@ -48,17 +49,17 @@ namespace osu.Game.Screens.SelectV2 private IBindable? starDifficultyBindable; private CancellationTokenSource? starDifficultyCancellationSource; - private BeatmapSetPanelBackground background = null!; + private PanelSetBackground background = null!; private OsuSpriteText titleText = null!; private OsuSpriteText artistText = null!; - private UpdateBeatmapSetButton updateButton = null!; + private PanelUpdateBeatmapButton updateButton = null!; private BeatmapSetOnlineStatusPill statusPill = null!; private ConstrainedIconContainer difficultyIcon = null!; private FillFlowContainer difficultyLine = null!; private StarRatingDisplay difficultyStarRating = null!; - private TopLocalRank difficultyRank = null!; + private PanelLocalRankDisplay difficultyRank = null!; private OsuSpriteText difficultyKeyCountText = null!; private OsuSpriteText difficultyName = null!; private OsuSpriteText difficultyAuthor = null!; @@ -80,7 +81,7 @@ namespace osu.Game.Screens.SelectV2 Colour = colourProvider.Background5, }; - Background = background = new BeatmapSetPanelBackground + Background = background = new PanelSetBackground { RelativeSizeAxes = Axes.Both, }; @@ -109,7 +110,7 @@ namespace osu.Game.Screens.SelectV2 Margin = new MarginPadding { Top = 5f }, Children = new Drawable[] { - updateButton = new UpdateBeatmapSetButton + updateButton = new PanelUpdateBeatmapButton { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -136,7 +137,7 @@ namespace osu.Game.Screens.SelectV2 Scale = new Vector2(8f / 9f), Margin = new MarginPadding { Right = 5f }, }, - difficultyRank = new TopLocalRank + difficultyRank = new PanelLocalRankDisplay { Scale = new Vector2(8f / 11), Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/SelectV2/PanelGroup.cs b/osu.Game/Screens/SelectV2/PanelGroup.cs index a5786b53c9..ac4857d2f3 100644 --- a/osu.Game/Screens/SelectV2/PanelGroup.cs +++ b/osu.Game/Screens/SelectV2/PanelGroup.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; @@ -18,7 +19,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { - public partial class PanelGroup : PanelBase + public partial class PanelGroup : Panel { public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.2f; diff --git a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs index ce46362133..4ef3bd724c 100644 --- a/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs +++ b/osu.Game/Screens/SelectV2/PanelGroupStarDifficulty.cs @@ -18,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { - public partial class PanelGroupStarDifficulty : PanelBase + public partial class PanelGroupStarDifficulty : Panel { [Resolved] private OsuColour colours { get; set; } = null!; diff --git a/osu.Game/Screens/SelectV2/TopLocalRank.cs b/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs similarity index 96% rename from osu.Game/Screens/SelectV2/TopLocalRank.cs rename to osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs index 2a72a05db7..588e7e650e 100644 --- a/osu.Game/Screens/SelectV2/TopLocalRank.cs +++ b/osu.Game/Screens/SelectV2/PanelLocalRankDisplay.cs @@ -19,7 +19,7 @@ using Realms; namespace osu.Game.Screens.SelectV2 { - public partial class TopLocalRank : CompositeDrawable + public partial class PanelLocalRankDisplay : CompositeDrawable { private BeatmapInfo? beatmap; @@ -48,7 +48,7 @@ namespace osu.Game.Screens.SelectV2 private readonly UpdateableRank updateable; - public TopLocalRank(BeatmapInfo? beatmap = null) + public PanelLocalRankDisplay(BeatmapInfo? beatmap = null) { AutoSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/SelectV2/BeatmapSetPanelBackground.cs b/osu.Game/Screens/SelectV2/PanelSetBackground.cs similarity index 97% rename from osu.Game/Screens/SelectV2/BeatmapSetPanelBackground.cs rename to osu.Game/Screens/SelectV2/PanelSetBackground.cs index 798acf62ee..99dbf90556 100644 --- a/osu.Game/Screens/SelectV2/BeatmapSetPanelBackground.cs +++ b/osu.Game/Screens/SelectV2/PanelSetBackground.cs @@ -14,7 +14,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { - public partial class BeatmapSetPanelBackground : ModelBackedDrawable + public partial class PanelSetBackground : ModelBackedDrawable { protected override double TransformDuration => 400; diff --git a/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs b/osu.Game/Screens/SelectV2/PanelUpdateBeatmapButton.cs similarity index 98% rename from osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs rename to osu.Game/Screens/SelectV2/PanelUpdateBeatmapButton.cs index ac7e3856ac..2a850321a6 100644 --- a/osu.Game/Screens/SelectV2/UpdateBeatmapSetButton.cs +++ b/osu.Game/Screens/SelectV2/PanelUpdateBeatmapButton.cs @@ -22,7 +22,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { - public partial class UpdateBeatmapSetButton : OsuAnimatedButton + public partial class PanelUpdateBeatmapButton : OsuAnimatedButton { private BeatmapSetInfo? beatmapSet; @@ -53,7 +53,7 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private IDialogOverlay? dialogOverlay { get; set; } - public UpdateBeatmapSetButton() + public PanelUpdateBeatmapButton() { Size = new Vector2(75f, 22f); } diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index e295656a21..67ca110dab 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -11,7 +11,6 @@ using osu.Game.Overlays.Mods; using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; -using osu.Game.Screens.SelectV2.Footer; namespace osu.Game.Screens.SelectV2 { @@ -77,9 +76,9 @@ namespace osu.Game.Screens.SelectV2 public override IReadOnlyList CreateFooterButtons() => new ScreenFooterButton[] { - new ScreenFooterButtonMods(modSelectOverlay) { Current = Mods }, - new ScreenFooterButtonRandom(), - new ScreenFooterButtonOptions(), + new FooterButtonMods(modSelectOverlay) { Current = Mods }, + new FooterButtonRandom(), + new FooterButtonOptions(), }; protected override void LoadComplete() From 6fe1695d39d4c9f774807ad3bc8623c45587d099 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 19:40:47 +0900 Subject: [PATCH 51/58] Use full namespace isntead of weird using statement --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 0afeaa9532..85a87b0dff 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -38,7 +38,6 @@ using osu.Game.Users.Drawables; using osuTK; using osuTK.Graphics; using osu.Game.Localisation; -using WebLocalisation = osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.OnlinePlay { @@ -561,7 +560,7 @@ namespace osu.Game.Screens.OnlinePlay Size = new Vector2(30, 30), Alpha = AllowEditing ? 1 : 0, Action = () => RequestEdit?.Invoke(Item), - TooltipText = WebLocalisation.CommonStrings.ButtonsEdit + TooltipText = Resources.Localisation.Web.CommonStrings.ButtonsEdit }, removeButton = new PlaylistRemoveButton { From 6021d85e633915a0092923ecf16f06fb1554ce66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:41:05 +0900 Subject: [PATCH 52/58] Add keywords for converted setting --- .../Settings/Sections/UserInterface/SongSelectSettings.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index cb0d738a2c..d15008f858 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface { LabelText = UserInterfaceStrings.ShowConvertedBeatmaps, Current = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), + Keywords = new[] { "converts", "converted" } }, new SettingsEnumDropdown { From 2ac1b8903727a49ababc1c89952895d1c98e7f1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:42:32 +0900 Subject: [PATCH 53/58] Make some test methods static for future reuse --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 24 +++++++++---------- .../SongSelect/TestSceneBeatmapLeaderboard.cs | 12 ++++++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index d8573b2d03..8132f8a841 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.SongSelect foreach (var rulesetInfo in rulesets.AvailableRulesets) { var instance = rulesetInfo.CreateInstance(); - var testBeatmap = createTestBeatmap(rulesetInfo); + var testBeatmap = CreateTestBeatmap(rulesetInfo); beatmaps.Add(testBeatmap); @@ -124,6 +124,12 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("reset mods", () => SelectedMods.SetDefault()); } + [Test] + public void TestTruncation() + { + selectBeatmap(CreateLongMetadata()); + } + [Test] public void TestNullBeatmap() { @@ -135,17 +141,11 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType().Any()); } - [Test] - public void TestTruncation() - { - selectBeatmap(createLongMetadata()); - } - [Test] public void TestBPMUpdates() { const double bpm = 120; - IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + IBeatmap beatmap = CreateTestBeatmap(new OsuRuleset().RulesetInfo); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / bpm }); OsuModDoubleTime doubleTime = null!; @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.SongSelect [TestCase(120, 120.4, "DT", "180")] public void TestVaryingBPM(double commonBpm, double otherBpm, string? mod, string expectedDisplay) { - IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + IBeatmap beatmap = CreateTestBeatmap(new OsuRuleset().RulesetInfo); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); beatmap.ControlPointInfo.Add(100, new TimingControlPoint { BeatLength = 60 * 1000 / otherBpm }); beatmap.ControlPointInfo.Add(200, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm }); @@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.SongSelect [TestCase] public void TestLengthUpdates() { - IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + IBeatmap beatmap = CreateTestBeatmap(new OsuRuleset().RulesetInfo); double drain = beatmap.CalculateDrainLength(); beatmap.BeatmapInfo.Length = drain; @@ -248,7 +248,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore); } - private IBeatmap createTestBeatmap(RulesetInfo ruleset) + public static IBeatmap CreateTestBeatmap(RulesetInfo ruleset) { List objects = new List(); for (double i = 0; i < 50000; i += 1000) @@ -274,7 +274,7 @@ namespace osu.Game.Tests.Visual.SongSelect }; } - private IBeatmap createLongMetadata() + public static IBeatmap CreateLongMetadata() { return new Beatmap { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 45381b3e02..70f2fb1361 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect private RulesetStore rulesetStore = null!; private BeatmapManager beatmapManager = null!; private PlaySongSelect songSelect = null!; + private LeaderboardManager leaderboardManager = null!; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -52,9 +53,10 @@ namespace osu.Game.Tests.Visual.SongSelect dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, null, dependencies.Get(), Resources, dependencies.Get(), Beatmap.Default)); dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, API)); dependencies.CacheAs(songSelect = new PlaySongSelect()); - Dependencies.Cache(Realm); dependencies.Cache(leaderboardManager = new LeaderboardManager()); + Dependencies.Cache(Realm); + return dependencies; } @@ -204,8 +206,8 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestGlobalScoresDisplay() { AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Global); - AddStep(@"New Scores", () => leaderboard.SetScores(generateSampleScores(new BeatmapInfo()))); - AddStep(@"New Scores with teams", () => leaderboard.SetScores(generateSampleScores(new BeatmapInfo()).Select(s => + AddStep(@"New Scores", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()))); + AddStep(@"New Scores with teams", () => leaderboard.SetScores(GenerateSampleScores(new BeatmapInfo()).Select(s => { s.User.Team = new APITeam(); return s; @@ -310,7 +312,7 @@ namespace osu.Game.Tests.Visual.SongSelect { AddStep(@"Import new scores", () => { - foreach (var score in generateSampleScores(beatmapInfo())) + foreach (var score in GenerateSampleScores(beatmapInfo())) scoreManager.Import(score); }); } @@ -326,7 +328,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void checkStoredCount(int expected) => AddUntilStep($"Total scores stored is {expected}", () => Realm.Run(r => r.All().Count(s => !s.DeletePending)), () => Is.EqualTo(expected)); - private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmapInfo) + public static ScoreInfo[] GenerateSampleScores(BeatmapInfo beatmapInfo) { return new[] { From 4c1f4a512cb89f36b4a30343267cc8cdb03c38a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:42:57 +0900 Subject: [PATCH 54/58] Avoid adding arbitrary background in `SongSelectComponentsTestScene` --- .../SongSelectV2/SongSelectComponentsTestScene.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelectV2/SongSelectComponentsTestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/SongSelectComponentsTestScene.cs index 8694722acc..9e9cd3505a 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/SongSelectComponentsTestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/SongSelectComponentsTestScene.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; using osu.Game.Graphics.Cursor; using osu.Game.Overlays; @@ -20,7 +19,6 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(10), }; private Container? resizeContainer; @@ -33,15 +31,9 @@ namespace osu.Game.Tests.Visual.SongSelectV2 { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(10), Width = relativeWidth, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background5, - }, Content } }; @@ -55,6 +47,12 @@ namespace osu.Game.Tests.Visual.SongSelectV2 }); } + protected override void LoadComplete() + { + base.LoadComplete(); + ChangeBackgroundColour(ColourProvider.Background6); + } + [SetUpSteps] public virtual void SetUpSteps() { From 1cca936e285b008d136d279a4ea8773416a4f4a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:43:32 +0900 Subject: [PATCH 55/58] Add global screen margin for new screen designs --- osu.Game/OsuGame.cs | 5 +++++ osu.Game/Screens/Footer/ScreenFooter.cs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 70a324cd8e..0c6a06a8fc 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -104,6 +104,11 @@ namespace osu.Game /// public static readonly Vector2 SHEAR = new Vector2(0.2f, 0); + /// + /// For elements placed close to the screen edge, this is the margin to leave to the edge. + /// + public const float SCREEN_EDGE_MARGIN = 12f; + public Toolbar Toolbar { get; private set; } private ChatOverlay chatOverlay; diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index f75250a832..94f4ceeb1a 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -104,14 +104,14 @@ namespace osu.Game.Screens.Footer }, BackButton = new ScreenBackButton { - Margin = new MarginPadding { Bottom = 15f, Left = 12f }, + Margin = new MarginPadding { Bottom = OsuGame.SCREEN_EDGE_MARGIN, Left = OsuGame.SCREEN_EDGE_MARGIN }, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Action = onBackPressed, }, hiddenButtonsContainer = new Container { - Margin = new MarginPadding { Left = 12f + ScreenBackButton.BUTTON_WIDTH + padding }, + Margin = new MarginPadding { Left = OsuGame.SCREEN_EDGE_MARGIN + ScreenBackButton.BUTTON_WIDTH + padding }, Y = 10f, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, From 376b4e89299f61b05f441ebdee1923a245f4aa66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:45:48 +0900 Subject: [PATCH 56/58] Disable masking of `Carousel` The default for carousels should be unmasked as their usage generally sees them overflowing outside their main usage area (see `bleed` variables). --- osu.Game/Graphics/Carousel/Carousel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/Carousel/Carousel.cs b/osu.Game/Graphics/Carousel/Carousel.cs index a9c8aecd6c..3a02eb7119 100644 --- a/osu.Game/Graphics/Carousel/Carousel.cs +++ b/osu.Game/Graphics/Carousel/Carousel.cs @@ -228,6 +228,7 @@ namespace osu.Game.Graphics.Carousel { InternalChild = Scroll = new CarouselScrollContainer { + Masking = false, RelativeSizeAxes = Axes.Both, }; From 07d0c7443c6f9749277377b882fbf3211d0d282e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 16 Apr 2025 18:46:37 +0900 Subject: [PATCH 57/58] Add animated fade when online status pill has an unknown status --- osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 7b3067e8d6..c6a3c7db3c 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -100,7 +100,7 @@ namespace osu.Game.Beatmaps.Drawables { if (Status == BeatmapOnlineStatus.None && !ShowUnknownStatus) { - Hide(); + this.FadeOut(animation_duration, Easing.OutQuint); return; } From a82bb5c2f6ae67ce9aa32656188bb286b5b4fef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 17 Apr 2025 10:36:04 +0200 Subject: [PATCH 58/58] Add theoretically-valid-but-practically-not commented-out test cases --- osu.Game.Rulesets.Mania.Tests/TestSceneReplayStability.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneReplayStability.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneReplayStability.cs index 497d8a18b8..b70657815c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneReplayStability.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneReplayStability.cs @@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Mania.Tests new object[] { 5f, -19d, HitResult.Perfect }, new object[] { 5f, -19.2d, HitResult.Perfect }, new object[] { 5f, -19.38d, HitResult.Perfect }, + // new object[] { 5f, -19.4d, HitResult.Perfect }, <- in theory this should work, in practice it does not (fails even before encode & rounding due to floating point precision issues) new object[] { 5f, -19.44d, HitResult.Great }, new object[] { 5f, -19.7d, HitResult.Great }, new object[] { 5f, -20d, HitResult.Great }, @@ -69,6 +70,7 @@ namespace osu.Game.Rulesets.Mania.Tests new object[] { 9.3f, 14d, HitResult.Perfect }, new object[] { 9.3f, 14.2d, HitResult.Perfect }, new object[] { 9.3f, 14.6d, HitResult.Perfect }, + // new object[] { 9.3f, 14.67d, HitResult.Perfect }, <- in theory this should work, in practice it does not (fails even before encode & rounding due to floating point precision issues) new object[] { 9.3f, 14.7d, HitResult.Great }, new object[] { 9.3f, 15d, HitResult.Great }, new object[] { 9.3f, 35d, HitResult.Great },