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

Merge branch 'master' into c-sharp-10

This commit is contained in:
Dean Herbert 2022-06-24 21:22:33 +09:00
commit 30eebf3511
38 changed files with 130 additions and 303 deletions

View File

@ -17,3 +17,5 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks. P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever. M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch
protected override string RulesetPrefix => "catch"; // todo: use CatchRuleset.SHORT_NAME; protected override string RulesetPrefix => "catch"; // todo: use CatchRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower(); protected override string ComponentName => Component.ToString().ToLowerInvariant();
} }
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME; protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower(); protected override string ComponentName => Component.ToString().ToLowerInvariant();
} }
public enum ManiaSkinComponents public enum ManiaSkinComponents

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Osu
protected override string RulesetPrefix => OsuRuleset.SHORT_NAME; protected override string RulesetPrefix => OsuRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower(); protected override string ComponentName => Component.ToString().ToLowerInvariant();
} }
} }

View File

@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
createDrawableRuleset(); createDrawableRuleset();
assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear); assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear);
AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLower()}", () => allMascotsIn(expectedStateAfterClear)); AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLowerInvariant()}", () => allMascotsIn(expectedStateAfterClear));
} }
private void setBeatmap(bool kiai = false) private void setBeatmap(bool kiai = false)
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{ {
TaikoMascotAnimationState[] mascotStates = null; TaikoMascotAnimationState[] mascotStates = null;
AddStep($"{judgementResult.Type.ToString().ToLower()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}", AddStep($"{judgementResult.Type.ToString().ToLowerInvariant()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}",
() => () =>
{ {
applyNewResult(judgementResult); applyNewResult(judgementResult);
@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray()); Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray());
}); });
AddAssert($"state is {expectedState.ToString().ToLower()}", () => mascotStates.All(state => state == expectedState)); AddAssert($"state is {expectedState.ToString().ToLowerInvariant()}", () => mascotStates.All(state => state == expectedState));
} }
private void applyNewResult(JudgementResult judgementResult) private void applyNewResult(JudgementResult judgementResult)

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Taiko
protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME; protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower(); protected override string ComponentName => Component.ToString().ToLowerInvariant();
} }
} }

View File

@ -139,10 +139,10 @@ namespace osu.Game.Rulesets.Taiko.UI
private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex) private static Texture getAnimationFrame(ISkin skin, TaikoMascotAnimationState state, int frameIndex)
{ {
var texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}{frameIndex}"); var texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}{frameIndex}");
if (frameIndex == 0 && texture == null) if (frameIndex == 0 && texture == null)
texture = skin.GetTexture($"pippidon{state.ToString().ToLower()}"); texture = skin.GetTexture($"pippidon{state.ToString().ToLowerInvariant()}");
return texture; return texture;
} }

View File

@ -192,7 +192,7 @@ namespace osu.Game.Tests.Gameplay
AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect }));
AddAssert("not failed", () => !processor.HasFailed); AddAssert("not failed", () => !processor.HasFailed);
AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied })); AddStep($"apply {resultApplied.ToString().ToLowerInvariant()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied }));
AddAssert("failed", () => processor.HasFailed); AddAssert("failed", () => processor.HasFailed);
} }

View File

@ -254,7 +254,7 @@ namespace osu.Game.Tests.NonVisual
Assert.That(File.Exists(Path.Combine(customPath, OsuGameBase.CLIENT_DATABASE_FILENAME))); Assert.That(File.Exists(Path.Combine(customPath, OsuGameBase.CLIENT_DATABASE_FILENAME)));
Directory.CreateDirectory(customPath2); Directory.CreateDirectory(customPath2);
File.Copy(Path.Combine(customPath, OsuGameBase.CLIENT_DATABASE_FILENAME), Path.Combine(customPath2, OsuGameBase.CLIENT_DATABASE_FILENAME)); File.WriteAllText(Path.Combine(customPath2, OsuGameBase.CLIENT_DATABASE_FILENAME), "I am a text");
// Fails because file already exists. // Fails because file already exists.
Assert.Throws<ArgumentException>(() => osu.Migrate(customPath2)); Assert.Throws<ArgumentException>(() => osu.Migrate(customPath2));

View File

@ -872,10 +872,10 @@ namespace osu.Game.Tests.Visual.SongSelect
return set != null; return set != null;
}); });
FilterableGroupedDifficultyIcon groupIcon = null; GroupedDifficultyIcon groupIcon = null;
AddUntilStep("Find group icon for different ruleset", () => AddUntilStep("Find group icon for different ruleset", () =>
{ {
return (groupIcon = set.ChildrenOfType<FilterableGroupedDifficultyIcon>() return (groupIcon = set.ChildrenOfType<GroupedDifficultyIcon>()
.FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.OnlineID == 3)) != null; .FirstOrDefault(icon => icon.Items.First().BeatmapInfo.Ruleset.OnlineID == 3)) != null;
}); });

View File

@ -66,14 +66,14 @@ namespace osu.Game.Tournament.Models
{ {
// use a sane default flag name based on acronym. // use a sane default flag name based on acronym.
if (val.OldValue.StartsWith(FlagName.Value, StringComparison.InvariantCultureIgnoreCase)) if (val.OldValue.StartsWith(FlagName.Value, StringComparison.InvariantCultureIgnoreCase))
FlagName.Value = val.NewValue.Length >= 2 ? val.NewValue?.Substring(0, 2).ToUpper() : string.Empty; FlagName.Value = val.NewValue.Length >= 2 ? val.NewValue?.Substring(0, 2).ToUpperInvariant() : string.Empty;
}; };
FullName.ValueChanged += val => FullName.ValueChanged += val =>
{ {
// use a sane acronym based on full name. // use a sane acronym based on full name.
if (val.OldValue.StartsWith(Acronym.Value, StringComparison.InvariantCultureIgnoreCase)) if (val.OldValue.StartsWith(Acronym.Value, StringComparison.InvariantCultureIgnoreCase))
Acronym.Value = val.NewValue.Length >= 3 ? val.NewValue?.Substring(0, 3).ToUpper() : string.Empty; Acronym.Value = val.NewValue.Length >= 3 ? val.NewValue?.Substring(0, 3).ToUpperInvariant() : string.Empty;
}; };
} }

View File

@ -49,10 +49,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
}; };
name = round.Name.GetBoundCopy(); name = round.Name.GetBoundCopy();
name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpper(), true); name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpperInvariant(), true);
description = round.Description.GetBoundCopy(); description = round.Description.GetBoundCopy();
description.BindValueChanged(n => textDescription.Text = round.Description.Value?.ToUpper(), true); description.BindValueChanged(n => textDescription.Text = round.Description.Value?.ToUpperInvariant(), true);
} }
} }
} }

View File

@ -198,7 +198,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
{ {
row.Add(new Sprite row.Add(new Sprite
{ {
Texture = textures.Get($"Mods/{mods.ToLower()}"), Texture = textures.Get($"Mods/{mods.ToLowerInvariant()}"),
Scale = new Vector2(0.5f) Scale = new Vector2(0.5f)
}); });
} }

View File

@ -64,13 +64,13 @@ namespace osu.Game.Beatmaps
{ {
base.LoadComplete(); base.LoadComplete();
currentRuleset.BindValueChanged(_ => updateTrackedBindables()); currentRuleset.BindValueChanged(_ => Scheduler.AddOnce(updateTrackedBindables));
currentMods.BindValueChanged(mods => currentMods.BindValueChanged(mods =>
{ {
modSettingChangeTracker?.Dispose(); modSettingChangeTracker?.Dispose();
updateTrackedBindables(); Scheduler.AddOnce(updateTrackedBindables);
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
modSettingChangeTracker.SettingChanged += _ => modSettingChangeTracker.SettingChanged += _ =>
@ -89,7 +89,9 @@ namespace osu.Game.Beatmaps
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated).</returns> /// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated).</returns>
public IBindable<StarDifficulty?> GetBindableDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) public IBindable<StarDifficulty?> GetBindableDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default)
{ {
var bindable = createBindable(beatmapInfo, currentRuleset.Value, currentMods.Value, cancellationToken); var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken);
updateBindable(bindable, currentRuleset.Value, currentMods.Value, cancellationToken);
lock (bindableUpdateLock) lock (bindableUpdateLock)
trackedBindables.Add(bindable); trackedBindables.Add(bindable);
@ -97,21 +99,6 @@ namespace osu.Game.Beatmaps
return bindable; return bindable;
} }
/// <summary>
/// Retrieves a bindable containing the star difficulty of a <see cref="IBeatmapInfo"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
/// </summary>
/// <remarks>
/// The bindable will not update to follow the currently-selected ruleset and mods or its settings.
/// </remarks>
/// <param name="beatmapInfo">The <see cref="IBeatmapInfo"/> to get the difficulty of.</param>
/// <param name="rulesetInfo">The <see cref="IRulesetInfo"/> to get the difficulty with. If <c>null</c>, the <paramref name="beatmapInfo"/>'s ruleset is used.</param>
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with. If <c>null</c>, no mods will be assumed.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="IBeatmapInfo"/>.</param>
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state.</returns>
public IBindable<StarDifficulty?> GetBindableDifficulty(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo, IEnumerable<Mod>? mods,
CancellationToken cancellationToken = default)
=> createBindable(beatmapInfo, rulesetInfo, mods, cancellationToken);
/// <summary> /// <summary>
/// Retrieves the difficulty of a <see cref="IBeatmapInfo"/>. /// Retrieves the difficulty of a <see cref="IBeatmapInfo"/>.
/// </summary> /// </summary>
@ -200,22 +187,6 @@ namespace osu.Game.Beatmaps
} }
} }
/// <summary>
/// Creates a new <see cref="BindableStarDifficulty"/> and triggers an initial value update.
/// </summary>
/// <param name="beatmapInfo">The <see cref="IBeatmapInfo"/> that star difficulty should correspond to.</param>
/// <param name="initialRulesetInfo">The initial <see cref="IRulesetInfo"/> to get the difficulty with.</param>
/// <param name="initialMods">The initial <see cref="Mod"/>s to get the difficulty with.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="IBeatmapInfo"/>.</param>
/// <returns>The <see cref="BindableStarDifficulty"/>.</returns>
private BindableStarDifficulty createBindable(IBeatmapInfo beatmapInfo, IRulesetInfo? initialRulesetInfo, IEnumerable<Mod>? initialMods,
CancellationToken cancellationToken)
{
var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken);
updateBindable(bindable, initialRulesetInfo, initialMods, cancellationToken);
return bindable;
}
/// <summary> /// <summary>
/// Updates the value of a <see cref="BindableStarDifficulty"/> with a given ruleset + mods. /// Updates the value of a <see cref="BindableStarDifficulty"/> with a given ruleset + mods.
/// </summary> /// </summary>

View File

@ -418,22 +418,24 @@ namespace osu.Game.Beatmaps
#region Implementation of IWorkingBeatmapCache #region Implementation of IWorkingBeatmapCache
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo? importedBeatmap) public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo? beatmapInfo)
{ {
// Detached sets don't come with files. // Detached sets don't come with files.
// If we seem to be missing files, now is a good time to re-fetch. // If we seem to be missing files, now is a good time to re-fetch.
if (importedBeatmap?.BeatmapSet?.Files.Count == 0) if (beatmapInfo?.IsManaged == true || beatmapInfo?.BeatmapSet?.Files.Count == 0)
{ {
Realm.Run(r => Realm.Run(r =>
{ {
var refetch = r.Find<BeatmapInfo>(importedBeatmap.ID)?.Detach(); var refetch = r.Find<BeatmapInfo>(beatmapInfo.ID)?.Detach();
if (refetch != null) if (refetch != null)
importedBeatmap = refetch; beatmapInfo = refetch;
}); });
} }
return workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); Debug.Assert(beatmapInfo?.IsManaged != true);
return workingBeatmapCache.GetWorkingBeatmap(beatmapInfo);
} }
void IWorkingBeatmapCache.Invalidate(BeatmapSetInfo beatmapSetInfo) => workingBeatmapCache.Invalidate(beatmapSetInfo); void IWorkingBeatmapCache.Invalidate(BeatmapSetInfo beatmapSetInfo) => workingBeatmapCache.Invalidate(beatmapSetInfo);

View File

@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
}; };
favouriteRequest.Failure += e => favouriteRequest.Failure += e =>
{ {
Logger.Error(e, $"Failed to {actionType.ToString().ToLower()} beatmap: {e.Message}"); Logger.Error(e, $"Failed to {actionType.ToString().ToLowerInvariant()} beatmap: {e.Message}");
Enabled.Value = true; Enabled.Value = true;
}; };

View File

@ -1,12 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Threading;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -16,19 +10,17 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip<DifficultyIconTooltipContent> public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip<DifficultyIconTooltipContent>, IHasCurrentValue<StarDifficulty>
{ {
private readonly Container iconContainer;
/// <summary> /// <summary>
/// Size of this difficulty icon. /// Size of this difficulty icon.
/// </summary> /// </summary>
@ -38,57 +30,53 @@ namespace osu.Game.Beatmaps.Drawables
set => iconContainer.Size = value; set => iconContainer.Size = value;
} }
[NotNull] /// <summary>
private readonly IBeatmapInfo beatmapInfo; /// Whether to display a tooltip on hover. Only works if a beatmap was provided at construction time.
/// </summary>
public bool ShowTooltip { get; set; } = true;
private readonly IBeatmapInfo? beatmap;
[CanBeNull]
private readonly IRulesetInfo ruleset; private readonly IRulesetInfo ruleset;
[CanBeNull] private Drawable background = null!;
private readonly IReadOnlyList<Mod> mods;
private readonly bool shouldShowTooltip; private readonly Container iconContainer;
private readonly bool performBackgroundDifficultyLookup; private readonly BindableWithCurrent<StarDifficulty> difficulty = new BindableWithCurrent<StarDifficulty>();
private readonly Bindable<StarDifficulty> difficultyBindable = new Bindable<StarDifficulty>(); public virtual Bindable<StarDifficulty> Current
private Drawable background;
/// <summary>
/// Creates a new <see cref="DifficultyIcon"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
/// </summary>
/// <param name="beatmapInfo">The beatmap to show the difficulty of.</param>
/// <param name="ruleset">The ruleset to show the difficulty with.</param>
/// <param name="mods">The mods to show the difficulty with.</param>
/// <param name="shouldShowTooltip">Whether to display a tooltip when hovered.</param>
/// <param name="performBackgroundDifficultyLookup">Whether to perform difficulty lookup (including calculation if necessary).</param>
public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo ruleset, [CanBeNull] IReadOnlyList<Mod> mods, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true)
: this(beatmapInfo, shouldShowTooltip, performBackgroundDifficultyLookup)
{ {
this.ruleset = ruleset ?? beatmapInfo.Ruleset; get => difficulty.Current;
this.mods = mods ?? Array.Empty<Mod>(); set => difficulty.Current = value;
}
/// <summary>
/// Creates a new <see cref="DifficultyIcon"/> that follows the currently-selected ruleset and mods.
/// </summary>
/// <param name="beatmapInfo">The beatmap to show the difficulty of.</param>
/// <param name="shouldShowTooltip">Whether to display a tooltip when hovered.</param>
/// <param name="performBackgroundDifficultyLookup">Whether to perform difficulty lookup (including calculation if necessary).</param>
public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true)
{
this.beatmapInfo = beatmapInfo ?? throw new ArgumentNullException(nameof(beatmapInfo));
this.shouldShowTooltip = shouldShowTooltip;
this.performBackgroundDifficultyLookup = performBackgroundDifficultyLookup;
AutoSizeAxes = Axes.Both;
InternalChild = iconContainer = new Container { Size = new Vector2(20f) };
} }
[Resolved] [Resolved]
private IRulesetStore rulesets { get; set; } private IRulesetStore rulesets { get; set; } = null!;
/// <summary>
/// Creates a new <see cref="DifficultyIcon"/>. Will use provided beatmap's <see cref="BeatmapInfo.StarRating"/> for initial value.
/// </summary>
/// <param name="beatmap">The beatmap to be displayed in the tooltip, and to be used for the initial star rating value.</param>
/// <param name="ruleset">An optional ruleset to be used for the icon display, in place of the beatmap's ruleset.</param>
public DifficultyIcon(IBeatmapInfo beatmap, IRulesetInfo? ruleset = null)
: this(ruleset ?? beatmap.Ruleset)
{
this.beatmap = beatmap;
Current.Value = new StarDifficulty(beatmap.StarRating, 0);
}
/// <summary>
/// Creates a new <see cref="DifficultyIcon"/> without an associated beatmap.
/// </summary>
/// <param name="ruleset">The ruleset to be used for the icon display.</param>
public DifficultyIcon(IRulesetInfo ruleset)
{
this.ruleset = ruleset;
AutoSizeAxes = Axes.Both;
InternalChild = iconContainer = new Container { Size = new Vector2(20f) };
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
@ -111,7 +99,6 @@ namespace osu.Game.Beatmaps.Drawables
Child = background = new Box Child = background = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.ForStarDifficulty(beatmapInfo.StarRating) // Default value that will be re-populated once difficulty calculation completes
}, },
}, },
new ConstrainedIconContainer new ConstrainedIconContainer
@ -124,17 +111,12 @@ namespace osu.Game.Beatmaps.Drawables
}, },
}; };
if (performBackgroundDifficultyLookup) Current.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars), true);
iconContainer.Add(new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmapInfo, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0));
else
difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarRating, 0);
difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars));
} }
private Drawable getRulesetIcon() private Drawable getRulesetIcon()
{ {
int? onlineID = (ruleset ?? beatmapInfo.Ruleset).OnlineID; int? onlineID = ruleset.OnlineID;
if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance)
return rulesetInstance.CreateIcon(); return rulesetInstance.CreateIcon();
@ -142,51 +124,10 @@ namespace osu.Game.Beatmaps.Drawables
return new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }; return new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle };
} }
ITooltip<DifficultyIconTooltipContent> IHasCustomTooltip<DifficultyIconTooltipContent>.GetCustomTooltip() => new DifficultyIconTooltip(); ITooltip<DifficultyIconTooltipContent> IHasCustomTooltip<DifficultyIconTooltipContent>.
GetCustomTooltip() => new DifficultyIconTooltip();
DifficultyIconTooltipContent IHasCustomTooltip<DifficultyIconTooltipContent>.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmapInfo, difficultyBindable) : null; DifficultyIconTooltipContent IHasCustomTooltip<DifficultyIconTooltipContent>.
TooltipContent => (ShowTooltip && beatmap != null ? new DifficultyIconTooltipContent(beatmap, Current) : null)!;
private class DifficultyRetriever : Component
{
public readonly Bindable<StarDifficulty> StarDifficulty = new Bindable<StarDifficulty>();
private readonly IBeatmapInfo beatmapInfo;
private readonly IRulesetInfo ruleset;
private readonly IReadOnlyList<Mod> mods;
private CancellationTokenSource difficultyCancellation;
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; }
public DifficultyRetriever(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, IReadOnlyList<Mod> mods)
{
this.beatmapInfo = beatmapInfo;
this.ruleset = ruleset;
this.mods = mods;
}
private IBindable<StarDifficulty?> localStarDifficulty;
[BackgroundDependencyLoader]
private void load()
{
difficultyCancellation = new CancellationTokenSource();
localStarDifficulty = ruleset != null
? difficultyCache.GetBindableDifficulty(beatmapInfo, ruleset, mods, difficultyCancellation.Token)
: difficultyCache.GetBindableDifficulty(beatmapInfo, difficultyCancellation.Token);
localStarDifficulty.BindValueChanged(d =>
{
if (d.NewValue is StarDifficulty diff)
StarDifficulty.Value = diff;
});
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
difficultyCancellation?.Cancel();
}
}
} }
} }

View File

@ -1,39 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
/// <summary>
/// A difficulty icon that contains a counter on the right-side of it.
/// </summary>
/// <remarks>
/// Used in cases when there are too many difficulty icons to show.
/// </remarks>
public class GroupedDifficultyIcon : DifficultyIcon
{
public GroupedDifficultyIcon(IEnumerable<IBeatmapInfo> beatmaps, IRulesetInfo ruleset, Color4 counterColour)
: base(beatmaps.OrderBy(b => b.StarRating).Last(), ruleset, null, false)
{
AddInternal(new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Padding = new MarginPadding { Left = Size.X },
Margin = new MarginPadding { Left = 2, Right = 5 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
Text = beatmaps.Count().ToString(),
Colour = counterColour,
});
}
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Database
/// <summary> /// <summary>
/// A user displayable name for the model type associated with this manager. /// A user displayable name for the model type associated with this manager.
/// </summary> /// </summary>
string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLowerInvariant()}";
/// <summary> /// <summary>
/// Fired when the user requests to view the resulting import. /// Fired when the user requests to view the resulting import.

View File

@ -201,6 +201,6 @@ namespace osu.Game.Database
public Action<Notification>? PostNotification { get; set; } public Action<Notification>? PostNotification { get; set; }
public virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; public virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLowerInvariant()}";
} }
} }

View File

@ -549,6 +549,6 @@ namespace osu.Game.Database
yield return f.Filename; yield return f.Filename;
} }
public virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLower()}"; public virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace(@"Info", "").ToLowerInvariant()}";
} }
} }

View File

@ -26,7 +26,7 @@ namespace osu.Game.Online.API.Requests
var req = base.CreateWebRequest(); var req = base.CreateWebRequest();
req.AddParameter("spotlight", spotlight.ToString()); req.AddParameter("spotlight", spotlight.ToString());
req.AddParameter("filter", sort.ToString().ToLower()); req.AddParameter("filter", sort.ToString().ToLowerInvariant());
return req; return req;
} }

View File

@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests
Ruleset = ruleset; Ruleset = ruleset;
} }
protected override string Target => Lookup != null ? $@"users/{Lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; protected override string Target => Lookup != null ? $@"users/{Lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLowerInvariant()}" : $@"me/{Ruleset?.ShortName}";
private enum LookupType private enum LookupType
{ {

View File

@ -48,7 +48,7 @@ namespace osu.Game.Online.Leaderboards
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = @"your personal best".ToUpper(), Text = @"your personal best".ToUpperInvariant(),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold), Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold),
}, },
scoreContainer = new Container scoreContainer = new Container

View File

@ -274,8 +274,10 @@ namespace osu.Game.Overlays.BeatmapSet
Alpha = 0.5f Alpha = 0.5f
} }
}, },
icon = new DifficultyIcon(beatmapInfo, shouldShowTooltip: false) icon = new DifficultyIcon(beatmapInfo)
{ {
ShowTooltip = false,
Current = { Value = new StarDifficulty(beatmapInfo.StarRating, 0) },
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(size - tile_icon_padding * 2), Size = new Vector2(size - tile_icon_padding * 2),

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -79,7 +80,7 @@ namespace osu.Game.Overlays.Chat
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(), Text = time.ToLocalTime().ToLocalisableString(@"dd MMMM yyyy").ToUpper(),
Font = OsuFont.Torus.With(size: TextSize, weight: FontWeight.SemiBold), Font = OsuFont.Torus.With(size: TextSize, weight: FontWeight.SemiBold),
Colour = colourProvider?.Content1 ?? Colour4.White, Colour = colourProvider?.Content1 ?? Colour4.White,
}, },

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
@ -146,7 +147,7 @@ namespace osu.Game.Overlays.News
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = date.ToString("d MMM yyyy").ToUpper(), Text = date.ToLocalisableString(@"d MMM yyyy").ToUpper(),
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
Margin = new MarginPadding Margin = new MarginPadding
{ {

View File

@ -109,15 +109,15 @@ namespace osu.Game.Overlays
: base(value) : base(value)
{ {
if (!(Value is Enum enumValue)) if (!(Value is Enum enumValue))
Text.Text = Value.ToString().ToLower(); Text.Text = Value.ToString().ToLowerInvariant();
else else
{ {
var localisableDescription = enumValue.GetLocalisableDescription(); var localisableDescription = enumValue.GetLocalisableDescription();
string nonLocalisableDescription = enumValue.GetDescription(); string nonLocalisableDescription = enumValue.GetDescription();
// If localisable == non-localisable, then we must have a basic string, so .ToLower() is used. // If localisable == non-localisable, then we must have a basic string, so .ToLowerInvariant() is used.
Text.Text = localisableDescription.Equals(nonLocalisableDescription) Text.Text = localisableDescription.Equals(nonLocalisableDescription)
? nonLocalisableDescription.ToLower() ? nonLocalisableDescription.ToLowerInvariant()
: localisableDescription; : localisableDescription;
} }

View File

@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Edit.Checks
if (edgeType == EdgeType.None) if (edgeType == EdgeType.None)
yield break; yield break;
string postfix = hitObject is IHasDuration ? edgeType.ToString().ToLower() : null; string postfix = hitObject is IHasDuration ? edgeType.ToString().ToLowerInvariant() : null;
if (maxVolume <= muted_threshold) if (maxVolume <= muted_threshold)
{ {

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Edit.Checks
} }
} }
private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLower().EndsWith); private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLowerInvariant().EndsWith);
private bool probablyHasAudioData(Stream data) => data.Length > min_bytes_threshold; private bool probablyHasAudioData(Stream data) => data.Length > min_bytes_threshold;
public class IssueTemplateTooShort : IssueTemplate public class IssueTemplateTooShort : IssueTemplate

View File

@ -1,78 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
{
public class ModeTypeInfo : OnlinePlayComposite
{
private const float height = 28;
private const float transition_duration = 100;
[Resolved]
private RulesetStore rulesets { get; set; }
private Container drawableRuleset;
public ModeTypeInfo()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
Container gameTypeContainer;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
LayoutDuration = 100,
Children = new[]
{
drawableRuleset = new Container
{
AutoSizeAxes = Axes.Both,
},
gameTypeContainer = new Container
{
AutoSizeAxes = Axes.Both,
},
},
};
Type.BindValueChanged(type => gameTypeContainer.Child = new DrawableGameType(type.NewValue) { Size = new Vector2(height) }, true);
Playlist.CollectionChanged += (_, __) => updateBeatmap();
updateBeatmap();
}
private void updateBeatmap()
{
var item = Playlist.FirstOrDefault();
var ruleset = item == null ? null : rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
if (item?.Beatmap != null && ruleset != null)
{
var mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray();
drawableRuleset.FadeIn(transition_duration);
drawableRuleset.Child = new DifficultyIcon(item.Beatmap, ruleset.RulesetInfo, mods) { Size = new Vector2(height) };
}
else
drawableRuleset.FadeOut(transition_duration);
}
}
}

View File

@ -266,7 +266,7 @@ namespace osu.Game.Screens.OnlinePlay
} }
if (beatmap != null) if (beatmap != null)
difficultyIconContainer.Child = new DifficultyIcon(beatmap, ruleset, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) }; difficultyIconContainer.Child = new DifficultyIcon(beatmap, ruleset) { Size = new Vector2(icon_height) };
else else
difficultyIconContainer.Clear(); difficultyIconContainer.Clear();

View File

@ -135,7 +135,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
new OsuSpriteText new OsuSpriteText
{ {
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12), Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12),
Text = title.ToUpper(), Text = title.ToUpperInvariant(),
}, },
content = new Container content = new Container
{ {

View File

@ -53,7 +53,9 @@ namespace osu.Game.Screens.Select.Carousel
private Action<BeatmapInfo> hideRequested; private Action<BeatmapInfo> hideRequested;
private Triangles triangles; private Triangles triangles;
private StarCounter starCounter; private StarCounter starCounter;
private DifficultyIcon difficultyIcon;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private BeatmapSetOverlay beatmapOverlay { get; set; } private BeatmapSetOverlay beatmapOverlay { get; set; }
@ -113,8 +115,9 @@ namespace osu.Game.Screens.Select.Carousel
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Children = new Drawable[] Children = new Drawable[]
{ {
new DifficultyIcon(beatmapInfo, shouldShowTooltip: false) difficultyIcon = new DifficultyIcon(beatmapInfo)
{ {
ShowTooltip = false,
Scale = new Vector2(1.8f), Scale = new Vector2(1.8f),
}, },
new FillFlowContainer new FillFlowContainer
@ -216,6 +219,8 @@ namespace osu.Game.Screens.Select.Carousel
starDifficultyBindable.BindValueChanged(d => starDifficultyBindable.BindValueChanged(d =>
{ {
starCounter.Current = (float)(d.NewValue?.Stars ?? 0); starCounter.Current = (float)(d.NewValue?.Stars ?? 0);
if (d.NewValue != null)
difficultyIcon.Current.Value = d.NewValue.Value;
}, true); }, true);
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Screens.Select.Carousel
public readonly CarouselBeatmap Item; public readonly CarouselBeatmap Item;
public FilterableDifficultyIcon(CarouselBeatmap item) public FilterableDifficultyIcon(CarouselBeatmap item)
: base(item.BeatmapInfo, performBackgroundDifficultyLookup: false) : base(item.BeatmapInfo)
{ {
filtered.BindTo(item.Filtered); filtered.BindTo(item.Filtered);
filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100)); filtered.ValueChanged += isFiltered => Schedule(() => this.FadeTo(isFiltered.NewValue ? 0.1f : 1, 100));

View File

@ -8,23 +8,42 @@ using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Screens.Select.Carousel namespace osu.Game.Screens.Select.Carousel
{ {
public class FilterableGroupedDifficultyIcon : GroupedDifficultyIcon /// <summary>
/// A difficulty icon that contains a counter on the right-side of it.
/// </summary>
/// <remarks>
/// Used in cases when there are too many difficulty icons to show.
/// </remarks>
public class GroupedDifficultyIcon : DifficultyIcon
{ {
public readonly List<CarouselBeatmap> Items; public readonly List<CarouselBeatmap> Items;
public FilterableGroupedDifficultyIcon(List<CarouselBeatmap> items, RulesetInfo ruleset) public GroupedDifficultyIcon(List<CarouselBeatmap> items, RulesetInfo ruleset)
: base(items.Select(i => i.BeatmapInfo).ToList(), ruleset, Color4.White) : base(items.OrderBy(b => b.BeatmapInfo.StarRating).Last().BeatmapInfo, ruleset)
{ {
Items = items; Items = items;
foreach (var item in items) foreach (var item in items)
item.Filtered.BindValueChanged(_ => Scheduler.AddOnce(updateFilteredDisplay)); item.Filtered.BindValueChanged(_ => Scheduler.AddOnce(updateFilteredDisplay));
AddInternal(new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Padding = new MarginPadding { Left = Size.X },
Margin = new MarginPadding { Left = 2, Right = 5 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
Text = items.Count.ToString(),
Colour = Color4.White,
});
updateFilteredDisplay(); updateFilteredDisplay();
} }

View File

@ -93,7 +93,7 @@ namespace osu.Game.Screens.Select.Carousel
return beatmaps.Count > maximum_difficulty_icons return beatmaps.Count > maximum_difficulty_icons
? beatmaps.GroupBy(b => b.BeatmapInfo.Ruleset) ? beatmaps.GroupBy(b => b.BeatmapInfo.Ruleset)
.Select(group => new FilterableGroupedDifficultyIcon(group.ToList(), group.Last().BeatmapInfo.Ruleset)) .Select(group => new GroupedDifficultyIcon(group.ToList(), group.Last().BeatmapInfo.Ruleset))
: beatmaps.Select(b => new FilterableDifficultyIcon(b)); : beatmaps.Select(b => new FilterableDifficultyIcon(b));
} }
} }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Screens.Select
{ {
foreach (Match match in query_syntax_regex.Matches(query)) foreach (Match match in query_syntax_regex.Matches(query))
{ {
string key = match.Groups["key"].Value.ToLower(); string key = match.Groups["key"].Value.ToLowerInvariant();
var op = parseOperator(match.Groups["op"].Value); var op = parseOperator(match.Groups["op"].Value);
string value = match.Groups["value"].Value; string value = match.Groups["value"].Value;