From 512cec3458f53c29b135d65d49a4af8b86be8fe2 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 00:08:16 +0800 Subject: [PATCH 001/215] Use unicode for playlists --- .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 23c713a2c1..4fccf45fda 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -13,11 +13,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Framework.Threading; 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.Online; using osu.Game.Online.Chat; @@ -105,10 +107,18 @@ namespace osu.Game.Screens.OnlinePlay private void refresh() { + var beatmapMetadata = Item.Beatmap.Value.Metadata ?? Item.Beatmap.Value.BeatmapSet?.Metadata; difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(Item.Beatmap.ToString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + var text = new List + { + new OsuSpriteText { Text = new RomanisableString(beatmapMetadata.ArtistUnicode, beatmapMetadata.Artist) }, + new OsuSpriteText { Text = " - " }, + new OsuSpriteText { Text = new RomanisableString(beatmapMetadata.TitleUnicode, beatmapMetadata.Title) }, + new OsuSpriteText { Text = $" ({beatmapMetadata.Author}) [{Item.Beatmap.Value.Version}]" } + }; + beatmapText.AddLink(text, LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); authorText.Clear(); From cfaaf2e83e3112ea16e786c9a7487a58e794cd17 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 09:52:25 +0800 Subject: [PATCH 002/215] Add ToRomanisableString() --- osu.Game/Beatmaps/BeatmapInfo.cs | 12 ++++++++---- osu.Game/Beatmaps/BeatmapMetadata.cs | 7 +++++++ .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 11 ++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index bf7906bd5c..c5c5bd208c 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.IO.Serialization; @@ -127,6 +128,8 @@ namespace osu.Game.Beatmaps // Metadata public string Version { get; set; } + public string VersionString => string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; + [JsonProperty("difficulty_rating")] public double StarDifficulty { get; set; } @@ -143,11 +146,12 @@ namespace osu.Game.Beatmaps Version }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override string ToString() - { - string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; + public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {VersionString}".Trim(); - return $"{Metadata ?? BeatmapSet?.Metadata} {version}".Trim(); + public RomanisableString ToRomanisableString() + { + var metadata = (Metadata ?? BeatmapSet?.Metadata).ToRomanisableString() ?? new RomanisableString(null, null); + return new RomanisableString($"{metadata.GetPreferred(true)} {VersionString}".Trim(), $"{metadata.GetPreferred(false)} {VersionString}".Trim()); } public bool Equals(BeatmapInfo other) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 858da8e602..02d1349794 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Users; @@ -71,6 +72,12 @@ namespace osu.Game.Beatmaps return $"{Artist} - {Title} {author}".Trim(); } + public RomanisableString ToRomanisableString() + { + string author = Author == null ? string.Empty : $"({Author})"; + return new RomanisableString($"{ArtistUnicode} - {TitleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim()); + } + [JsonIgnore] public string[] SearchableTerms => new[] { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 4fccf45fda..69173bd53b 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -107,18 +107,11 @@ namespace osu.Game.Screens.OnlinePlay private void refresh() { - var beatmapMetadata = Item.Beatmap.Value.Metadata ?? Item.Beatmap.Value.BeatmapSet?.Metadata; difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - var text = new List - { - new OsuSpriteText { Text = new RomanisableString(beatmapMetadata.ArtistUnicode, beatmapMetadata.Artist) }, - new OsuSpriteText { Text = " - " }, - new OsuSpriteText { Text = new RomanisableString(beatmapMetadata.TitleUnicode, beatmapMetadata.Title) }, - new OsuSpriteText { Text = $" ({beatmapMetadata.Author}) [{Item.Beatmap.Value.Version}]" } - }; - beatmapText.AddLink(text, LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + beatmapText.AddLink(new List{ new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() } } + , LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); authorText.Clear(); From 1339c126a49d9d52def6ef031959812f3ea48794 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 09:54:50 +0800 Subject: [PATCH 003/215] Remove unused using --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 69173bd53b..ca8e13d10a 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; From 646403b826da7d5b3e2600ea93ac2ab2c01e9346 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 10:00:17 +0800 Subject: [PATCH 004/215] Fix CI errors --- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index c5c5bd208c..d5a1e2491b 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -150,7 +150,7 @@ namespace osu.Game.Beatmaps public RomanisableString ToRomanisableString() { - var metadata = (Metadata ?? BeatmapSet?.Metadata).ToRomanisableString() ?? new RomanisableString(null, null); + var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null); return new RomanisableString($"{metadata.GetPreferred(true)} {VersionString}".Trim(), $"{metadata.GetPreferred(false)} {VersionString}".Trim()); } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index ca8e13d10a..fe5876e732 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -109,8 +109,8 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(new List{ new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() } } - , LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + beatmapText.AddLink(new List { new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() } }, + LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); authorText.Clear(); From 38a7c590c4d07ac17c9746c8feac2aa4a59c63af Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 20:57:25 +0800 Subject: [PATCH 005/215] Make versionString private --- osu.Game/Beatmaps/BeatmapInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index d5a1e2491b..36cb97e8d7 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -128,7 +128,7 @@ namespace osu.Game.Beatmaps // Metadata public string Version { get; set; } - public string VersionString => string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; + private string versionString => string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]"; [JsonProperty("difficulty_rating")] public double StarDifficulty { get; set; } @@ -146,12 +146,12 @@ namespace osu.Game.Beatmaps Version }.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty()).Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {VersionString}".Trim(); + public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim(); public RomanisableString ToRomanisableString() { var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null); - return new RomanisableString($"{metadata.GetPreferred(true)} {VersionString}".Trim(), $"{metadata.GetPreferred(false)} {VersionString}".Trim()); + return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); } public bool Equals(BeatmapInfo other) From 488001d5701b51db7405e5ebd7c3ab4174e9a699 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sun, 18 Apr 2021 20:58:08 +0800 Subject: [PATCH 006/215] Support SpriteText for LinkFlowContainer --- .../Graphics/Containers/LinkFlowContainer.cs | 23 +++++++++++++++++++ osu.Game/Online/Chat/DrawableLinkCompiler.cs | 5 ++++ .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 914c8ff78d..32efcce80c 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -67,6 +67,13 @@ namespace osu.Game.Graphics.Containers createLink(text, new LinkDetails(action, linkArgument), tooltipText); } + public void AddLink(SpriteText text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) + { + AddArbitraryDrawable(text); + + createLink(text, new LinkDetails(action, linkArgument), tooltipText); + } + public void AddUserLink(User user, Action creationParameters = null) => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); @@ -86,6 +93,22 @@ namespace osu.Game.Graphics.Containers }); } + private void createLink(Drawable drawable, LinkDetails link, string tooltipText, Action action = null) + { + AddInternal(new DrawableLinkCompiler(drawable) + { + RelativeSizeAxes = Axes.Both, + TooltipText = tooltipText, + Action = () => + { + if (action != null) + action(); + else + game?.HandleLink(link); + }, + }); + } + // We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used. // However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation. // Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow. diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index d27a3fbffe..7675eab4c0 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -31,6 +31,11 @@ namespace osu.Game.Online.Chat Parts = parts.ToList(); } + public DrawableLinkCompiler(Drawable part) + { + Parts = new List { part }; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index fe5876e732..a92ca610f3 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(new List { new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() } }, + beatmapText.AddLink(new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() }, LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); authorText.Clear(); From ef3801b5dd619d1fb0c5edf32c4d683ad26bd4ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Apr 2021 15:29:26 +0900 Subject: [PATCH 007/215] Add helper method supporting RomanisableString --- .../Graphics/Containers/LinkFlowContainer.cs | 34 ++++++------------- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 4 +-- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 32efcce80c..3a2f9d5a78 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -7,7 +7,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Graphics.Sprites; using osu.Game.Users; namespace osu.Game.Graphics.Containers @@ -59,6 +62,14 @@ namespace osu.Game.Graphics.Containers public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); + public void AddLink(RomanisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) + { + var spriteText = new OsuSpriteText { Text = text }; + + AddText(spriteText, creationParameters); + createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText); + } + public void AddLink(IEnumerable text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) { foreach (var t in text) @@ -67,13 +78,6 @@ namespace osu.Game.Graphics.Containers createLink(text, new LinkDetails(action, linkArgument), tooltipText); } - public void AddLink(SpriteText text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null) - { - AddArbitraryDrawable(text); - - createLink(text, new LinkDetails(action, linkArgument), tooltipText); - } - public void AddUserLink(User user, Action creationParameters = null) => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); @@ -93,22 +97,6 @@ namespace osu.Game.Graphics.Containers }); } - private void createLink(Drawable drawable, LinkDetails link, string tooltipText, Action action = null) - { - AddInternal(new DrawableLinkCompiler(drawable) - { - RelativeSizeAxes = Axes.Both, - TooltipText = tooltipText, - Action = () => - { - if (action != null) - action(); - else - game?.HandleLink(link); - }, - }); - } - // We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used. // However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation. // Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow. diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index a92ca610f3..38a9ace619 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -18,7 +18,6 @@ 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.Online; using osu.Game.Online.Chat; @@ -109,8 +108,7 @@ namespace osu.Game.Screens.OnlinePlay difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; beatmapText.Clear(); - beatmapText.AddLink(new OsuSpriteText { Text = Item.Beatmap.Value.ToRomanisableString() }, - LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); + beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString()); authorText.Clear(); From 25c7dc9ef0f7716a08878733b9cba18063cfef48 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 19 Apr 2021 15:30:11 +0900 Subject: [PATCH 008/215] Revert unnecessary change --- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 7675eab4c0..d27a3fbffe 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -31,11 +31,6 @@ namespace osu.Game.Online.Chat Parts = parts.ToList(); } - public DrawableLinkCompiler(Drawable part) - { - Parts = new List { part }; - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { From 2a7ef1f80f037cbf393acbf23f1bd06ee4cbc4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 19 Apr 2021 19:27:35 +0200 Subject: [PATCH 009/215] Use more general type --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 3a2f9d5a78..054febeec3 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Graphics.Containers public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); - public void AddLink(RomanisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) + public void AddLink(LocalisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) { var spriteText = new OsuSpriteText { Text = text }; From 0f08c2a4799d3002861b199205c0e20e4498ff23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 11:44:29 +0300 Subject: [PATCH 010/215] Add star rating display underneath the beatmap metadata --- .../Screens/Play/BeatmapMetadataDisplay.cs | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index c56344a8fb..7ad634afed 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -12,8 +12,10 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Ranking.Expanded; using osuTK; namespace osu.Game.Screens.Play @@ -30,6 +32,9 @@ namespace osu.Game.Screens.Play public IBindable> Mods => mods; + [Resolved] + private IBindable ruleset { get; set; } + public bool Loading { set @@ -51,10 +56,12 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(BeatmapDifficultyCache difficultyCache) { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var starDifficulty = difficultyCache.GetDifficultyAsync(beatmap.BeatmapInfo, ruleset.Value, mods.Value).Result; + AutoSizeAxes = Axes.Both; Children = new Drawable[] { @@ -107,16 +114,29 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - new OsuSpriteText + new FillFlowContainer { - Text = beatmap?.BeatmapInfo?.Version, - Font = OsuFont.GetFont(size: 26, italics: true), - Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, - Margin = new MarginPadding + Origin = Anchor.TopCentre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + Margin = new MarginPadding { Bottom = 40 }, + Children = new Drawable[] { - Bottom = 40 - }, + new OsuSpriteText + { + Text = beatmap?.BeatmapInfo?.Version, + Font = OsuFont.GetFont(size: 26, italics: true), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + new StarRatingDisplay(starDifficulty) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } }, new GridContainer { From 7b7e7a86bf70423464fa078b10b52301790ad5dc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 11:46:28 +0300 Subject: [PATCH 011/215] Allow null logo facade --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 7ad634afed..8e760c38ba 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -27,7 +28,7 @@ namespace osu.Game.Screens.Play { private readonly WorkingBeatmap beatmap; private readonly Bindable> mods; - private readonly Drawable facade; + private readonly Drawable logoFacade; private LoadingSpinner loading; public IBindable> Mods => mods; @@ -46,10 +47,10 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable facade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, [CanBeNull] Drawable logoFacade) { this.beatmap = beatmap; - this.facade = facade; + this.logoFacade = logoFacade; this.mods = new Bindable>(); this.mods.BindTo(mods); @@ -73,11 +74,11 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - facade.With(d => + logoFacade?.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; - }), + }) ?? Drawable.Empty(), new OsuSpriteText { Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), From 169a28340286aeca24e73f47ce76c190c3f7c87d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 11:46:42 +0300 Subject: [PATCH 012/215] Add visual test scene --- .../TestSceneBeatmapMetadataDisplay.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs new file mode 100644 index 0000000000..02c9c6b7a3 --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -0,0 +1,78 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play; +using osuTK; + +namespace osu.Game.Tests.Visual.SongSelect +{ + [System.ComponentModel.Description("player loader beatmap metadata")] + public class TestSceneBeatmapMetadataDisplay : OsuTestScene + { + private BeatmapMetadataDisplay display; + + [Resolved] + private BeatmapManager manager { get; set; } + + private IReadOnlyList randomMods => Ruleset.Value.CreateInstance() + .GetAllMods() + .OrderBy(_ => RNG.Next()) + .Take(5) + .ToList(); + + private void createDisplay(Func getBeatmap) + { + AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), null) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + }); + + AddToggleStep("trigger loading", v => display.Loading = v); + } + + [Test] + public void TestLocal([Values("Beatmap", "Some long title and stuff")] + string title, + [Values("Trial", "Some1's very hardest difficulty")] + string version) + { + createDisplay(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = title, + }, + Version = version, + StarDifficulty = RNG.NextDouble(0, 10), + } + })); + } + + [Test] + public void TestRandomFromDatabase() + { + createDisplay(() => + { + var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; + var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; + + return manager.GetWorkingBeatmap(randomBeatmap); + }); + } + } +} From b4801faf32bed6a2bc279305d4237930f60a683d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 11:57:12 +0300 Subject: [PATCH 013/215] Pass ruleset info to constructor instead Follows the way working beatmap is passed, not sure why mods are passed as a bindable though, don't wanna bother too much with that. --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 2 +- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 9 ++++----- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 02c9c6b7a3..ee87877860 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void createDisplay(Func getBeatmap) { - AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), null) + AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), Ruleset.Value, new Bindable>(randomMods), null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 8e760c38ba..daaf3b73cb 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -27,15 +27,13 @@ namespace osu.Game.Screens.Play public class BeatmapMetadataDisplay : Container { private readonly WorkingBeatmap beatmap; + private readonly RulesetInfo ruleset; private readonly Bindable> mods; private readonly Drawable logoFacade; private LoadingSpinner loading; public IBindable> Mods => mods; - [Resolved] - private IBindable ruleset { get; set; } - public bool Loading { set @@ -47,9 +45,10 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, [CanBeNull] Drawable logoFacade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, RulesetInfo ruleset, Bindable> mods, [CanBeNull] Drawable logoFacade) { this.beatmap = beatmap; + this.ruleset = ruleset; this.logoFacade = logoFacade; this.mods = new Bindable>(); @@ -61,7 +60,7 @@ namespace osu.Game.Screens.Play { var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); - var starDifficulty = difficultyCache.GetDifficultyAsync(beatmap.BeatmapInfo, ruleset.Value, mods.Value).Result; + var starDifficulty = difficultyCache.GetDifficultyAsync(beatmap.BeatmapInfo, ruleset, mods.Value).Result; AutoSizeAxes = Axes.Both; Children = new Drawable[] diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ce580e2b53..066ca25790 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -134,7 +134,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, }).WithChildren(new Drawable[] { - MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Mods, content.LogoFacade) + MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Ruleset.Value, Mods, content.LogoFacade) { Alpha = 0, Anchor = Anchor.Centre, From 0410edecaff273d81d023af07044bbd46a88b9a5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 12:55:07 +0300 Subject: [PATCH 014/215] Refactor `StarRatingDisplay` to be mutable with a current bindable --- .../Ranking/TestSceneStarRatingDisplay.cs | 15 +++- .../Ranking/Expanded/StarRatingDisplay.cs | 87 ++++++++++++------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index d0067c3396..a043f506c1 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Screens.Ranking.Expanded; @@ -10,8 +12,11 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - public TestSceneStarRatingDisplay() + [SetUp] + public void SetUp() => Schedule(() => { + StarRatingDisplay changingStarRating; + Child = new FillFlowContainer { Anchor = Anchor.Centre, @@ -25,8 +30,14 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(5.67, 0)), new StarRatingDisplay(new StarDifficulty(6.78, 0)), new StarRatingDisplay(new StarDifficulty(10.11, 0)), + changingStarRating = new StarRatingDisplay(), } }; - } + + Scheduler.AddDelayed(() => + { + changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); + }, 500, true); + }); } } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index f7e50fdc8a..a1f48fa811 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -3,12 +3,14 @@ using System.Globalization; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -20,17 +22,29 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { - private readonly StarDifficulty difficulty; + private Box background; + private OsuTextFlowContainer textFlow; + + [Resolved] + private OsuColour colours { get; set; } + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty) + public StarRatingDisplay(StarDifficulty starDifficulty = default) { - difficulty = starDifficulty; + Current.Value = starDifficulty; } [BackgroundDependencyLoader] @@ -38,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded { AutoSizeAxes = Axes.Both; - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - - ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - InternalChildren = new Drawable[] { new CircularContainer @@ -55,10 +60,9 @@ namespace osu.Game.Screens.Ranking.Expanded Masking = true, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour }, } }, @@ -78,32 +82,51 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }) + } } } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + } + + private void updateDisplay(StarDifficulty difficulty) + { + var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + + textFlow.Clear(); + + textFlow.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + textFlow.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + } } } From 43090067da8a199eca6e2d4202d0bef1eae7fe5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 12:59:59 +0300 Subject: [PATCH 015/215] Use `BeatmapDifficultyCache.GetBindableDifficulty(...)` instead --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index daaf3b73cb..54c739bd9f 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -55,12 +55,14 @@ namespace osu.Game.Screens.Play this.mods.BindTo(mods); } + private IBindable starDifficulty; + [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache difficultyCache) { - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + StarRatingDisplay starRatingDisplay; - var starDifficulty = difficultyCache.GetDifficultyAsync(beatmap.BeatmapInfo, ruleset, mods.Value).Result; + var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -131,7 +133,7 @@ namespace osu.Game.Screens.Play Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - new StarRatingDisplay(starDifficulty) + starRatingDisplay = new StarRatingDisplay { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -179,6 +181,13 @@ namespace osu.Game.Screens.Play } }; + starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); + starDifficulty.BindValueChanged(difficulty => + { + if (difficulty.NewValue is StarDifficulty diff) + starRatingDisplay.Current.Value = diff; + }, true); + Loading = true; } From dca5efc59afc5876b794e6cb3610e4cb18f3a64d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 13:00:39 +0300 Subject: [PATCH 016/215] Remove no longer necessary ruleset info requirement --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 2 +- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 5 +---- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index ee87877860..02c9c6b7a3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void createDisplay(Func getBeatmap) { - AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), Ruleset.Value, new Bindable>(randomMods), null) + AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), null) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 54c739bd9f..670d99c462 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -13,7 +13,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking.Expanded; @@ -27,7 +26,6 @@ namespace osu.Game.Screens.Play public class BeatmapMetadataDisplay : Container { private readonly WorkingBeatmap beatmap; - private readonly RulesetInfo ruleset; private readonly Bindable> mods; private readonly Drawable logoFacade; private LoadingSpinner loading; @@ -45,10 +43,9 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, RulesetInfo ruleset, Bindable> mods, [CanBeNull] Drawable logoFacade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, [CanBeNull] Drawable logoFacade) { this.beatmap = beatmap; - this.ruleset = ruleset; this.logoFacade = logoFacade; this.mods = new Bindable>(); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 066ca25790..ce580e2b53 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -134,7 +134,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, }).WithChildren(new Drawable[] { - MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Ruleset.Value, Mods, content.LogoFacade) + MetadataInfo = new BeatmapMetadataDisplay(Beatmap.Value, Mods, content.LogoFacade) { Alpha = 0, Anchor = Anchor.Centre, From 26c0010fe659915ebebb152be37032b6003b4ff3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 13:03:50 +0300 Subject: [PATCH 017/215] Fix test not handling 0 beatmap sets --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 02c9c6b7a3..5ea7a0e83b 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -68,6 +68,9 @@ namespace osu.Game.Tests.Visual.SongSelect createDisplay(() => { var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (allBeatmapSets.Count == 0) + return manager.DefaultBeatmap; + var randomBeatmapSet = allBeatmapSets[RNG.Next(0, allBeatmapSets.Count - 1)]; var randomBeatmap = randomBeatmapSet.Beatmaps[RNG.Next(0, randomBeatmapSet.Beatmaps.Count - 1)]; From d9605e807049ceabb9af5a53568738a1a424c341 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:18:23 +0300 Subject: [PATCH 018/215] Remove test scene description --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 5ea7a0e83b..381ccd2dd3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -16,7 +16,6 @@ using osuTK; namespace osu.Game.Tests.Visual.SongSelect { - [System.ComponentModel.Description("player loader beatmap metadata")] public class TestSceneBeatmapMetadataDisplay : OsuTestScene { private BeatmapMetadataDisplay display; From 3575d9847cd120941ad3737ff140c01184edd4e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:21:29 +0300 Subject: [PATCH 019/215] Use regular test steps rather than one-time set up and scheduling --- .../Visual/Ranking/TestSceneStarRatingDisplay.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index a043f506c1..2ff664a0d9 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -12,12 +12,12 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - [SetUp] - public void SetUp() => Schedule(() => + [Test] + public void TestDisplay() { - StarRatingDisplay changingStarRating; + StarRatingDisplay changingStarRating = null; - Child = new FillFlowContainer + AddStep("load displays", () => Child = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -32,12 +32,12 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(10.11, 0)), changingStarRating = new StarRatingDisplay(), } - }; + }); - Scheduler.AddDelayed(() => + AddRepeatStep("change bottom rating", () => { changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); - }, 500, true); - }); + }, 10); + } } } From a75347cb2a5dc933b9f2952d3449208379eee935 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:43:17 +0300 Subject: [PATCH 020/215] Remove nullable facade logic --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 670d99c462..98829d079b 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -43,7 +42,7 @@ namespace osu.Game.Screens.Play } } - public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, [CanBeNull] Drawable logoFacade) + public BeatmapMetadataDisplay(WorkingBeatmap beatmap, Bindable> mods, Drawable logoFacade) { this.beatmap = beatmap; this.logoFacade = logoFacade; @@ -72,11 +71,11 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new[] { - logoFacade?.With(d => + logoFacade.With(d => { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; - }) ?? Drawable.Empty(), + }), new OsuSpriteText { Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), From ca55287dd07a9d958e35c738d331dad21b8295ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:43:45 +0300 Subject: [PATCH 021/215] Pass empty facade and replace random property with method instead --- .../SongSelect/TestSceneBeatmapMetadataDisplay.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 381ccd2dd3..552d19ac26 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -23,15 +23,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Resolved] private BeatmapManager manager { get; set; } - private IReadOnlyList randomMods => Ruleset.Value.CreateInstance() - .GetAllMods() - .OrderBy(_ => RNG.Next()) - .Take(5) - .ToList(); - private void createDisplay(Func getBeatmap) { - AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), null) + AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(getRandomMods()), Empty()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -76,5 +70,11 @@ namespace osu.Game.Tests.Visual.SongSelect return manager.GetWorkingBeatmap(randomBeatmap); }); } + + private IReadOnlyList getRandomMods() => Ruleset.Value.CreateInstance() + .GetAllMods() + .OrderBy(_ => RNG.Next()) + .Take(5) + .ToList(); } } From 655e8d3d867c5a2956caeea928e0704c98b580e6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:44:28 +0300 Subject: [PATCH 022/215] Remove pattern-matching on nullable with simple `.HasValue`/`.Value` --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 98829d079b..d31033ef15 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -180,8 +180,8 @@ namespace osu.Game.Screens.Play starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); starDifficulty.BindValueChanged(difficulty => { - if (difficulty.NewValue is StarDifficulty diff) - starRatingDisplay.Current.Value = diff; + if (difficulty.NewValue.HasValue) + starRatingDisplay.Current.Value = difficulty.NewValue.Value; }, true); Loading = true; From c52f1733be73b49af4f3dea4257fd0b9eaa5610a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 21:13:18 +0300 Subject: [PATCH 023/215] Apply further refactoring to star rating display UX-wise --- .../Screens/Play/BeatmapMetadataDisplay.cs | 6 +- .../Ranking/Expanded/StarRatingDisplay.cs | 106 +++++++++++++----- 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index d31033ef15..0164fe9179 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -178,11 +178,7 @@ namespace osu.Game.Screens.Play }; starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); - starDifficulty.BindValueChanged(difficulty => - { - if (difficulty.NewValue.HasValue) - starRatingDisplay.Current.Value = difficulty.NewValue.Value; - }, true); + starDifficulty.BindValueChanged(d => starRatingDisplay.Current.Value = d.NewValue, true); Loading = true; } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index a1f48fa811..77c43ad863 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -22,27 +22,50 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; - private OsuTextFlowContainer textFlow; + private OsuSpriteText wholePart; + private OsuSpriteText fractionPart; + + private double displayedStarRating; + + protected double DisplayedStarRating + { + get => displayedStarRating; + set + { + displayedStarRating = value; + + var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + wholePart.Text = starRatingParts[0]; + fractionPart.Text = starRatingParts[1]; + } + } [Resolved] private OsuColour colours { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; } + /// + /// Creates a new without any set, displaying a placeholder until is changed. + /// + public StarRatingDisplay() + { + } + /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty = default) + public StarRatingDisplay(StarDifficulty starDifficulty) { Current.Value = starDifficulty; } @@ -82,13 +105,40 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, + Children = new[] + { + wholePart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + fractionPart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + } + } } } } @@ -98,35 +148,31 @@ namespace osu.Game.Screens.Ranking.Expanded protected override void LoadComplete() { base.LoadComplete(); - Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + + Current.BindValueChanged(_ => updateDisplay(), true); + FinishTransforms(true); } - private void updateDisplay(StarDifficulty difficulty) + private void updateDisplay() { - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + const double duration = 400; + const Easing easing = Easing.OutQuint; - background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + ColourInfo backgroundColour; - textFlow.Clear(); - - textFlow.AddText($"{wholePart}", s => + if (Current.Value == null) + backgroundColour = Color4.SlateGray.Opacity(0.3f); + else { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); + var rating = Current.Value.Value.DifficultyRating; - textFlow.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); + backgroundColour = rating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(rating); + } + + background.FadeColour(backgroundColour, duration, easing); + this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing); } } } From f701c331f285834f4814b4e070384a54637214f4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 21:15:15 +0300 Subject: [PATCH 024/215] Add initial fade in to the metadata display Avoids first frame discrepancies from appearing in the test scene, those can be delt with later on, if needed. --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 552d19ac26..f41180acf2 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -30,8 +30,10 @@ namespace osu.Game.Tests.Visual.SongSelect Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(1.5f), + Alpha = 0f, }); + AddStep("fade in", () => display.FadeIn(400, Easing.OutQuint)); AddToggleStep("trigger loading", v => display.Loading = v); } From 8fba655d2e9007449f19b67abf2876ce61656b67 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 21:20:17 +0300 Subject: [PATCH 025/215] Allow changing ruleset during test --- .../TestSceneBeatmapMetadataDisplay.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index f41180acf2..0fbf42da6f 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osuTK; @@ -23,7 +24,18 @@ namespace osu.Game.Tests.Visual.SongSelect [Resolved] private BeatmapManager manager { get; set; } - private void createDisplay(Func getBeatmap) + [Resolved] + private RulesetStore rulesets { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + foreach (var ruleset in rulesets.AvailableRulesets) + AddStep($"switch to {ruleset.Name}", () => Ruleset.Value = ruleset); + } + + private void createTest(Func getBeatmap) { AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(getRandomMods()), Empty()) { @@ -43,7 +55,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Values("Trial", "Some1's very hardest difficulty")] string version) { - createDisplay(() => CreateWorkingBeatmap(new Beatmap + createTest(() => CreateWorkingBeatmap(new Beatmap { BeatmapInfo = { @@ -60,7 +72,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestRandomFromDatabase() { - createDisplay(() => + createTest(() => { var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); if (allBeatmapSets.Count == 0) From 0c973feb53f1e76a593994340d522e1afb439e46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:34:21 +0900 Subject: [PATCH 026/215] Tidy up test scene --- .../TestSceneBeatmapMetadataDisplay.cs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 0fbf42da6f..e80c453da9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -35,27 +35,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep($"switch to {ruleset.Name}", () => Ruleset.Value = ruleset); } - private void createTest(Func getBeatmap) - { - AddStep("setup display", () => Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(getRandomMods()), Empty()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.5f), - Alpha = 0f, - }); - - AddStep("fade in", () => display.FadeIn(400, Easing.OutQuint)); - AddToggleStep("trigger loading", v => display.Loading = v); - } - [Test] public void TestLocal([Values("Beatmap", "Some long title and stuff")] string title, [Values("Trial", "Some1's very hardest difficulty")] string version) { - createTest(() => CreateWorkingBeatmap(new Beatmap + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap { BeatmapInfo = { @@ -72,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestRandomFromDatabase() { - createTest(() => + showMetadataForBeatmap(() => { var allBeatmapSets = manager.GetAllUsableBeatmapSets(IncludedDetails.Minimal); if (allBeatmapSets.Count == 0) @@ -85,10 +71,23 @@ namespace osu.Game.Tests.Visual.SongSelect }); } - private IReadOnlyList getRandomMods() => Ruleset.Value.CreateInstance() - .GetAllMods() - .OrderBy(_ => RNG.Next()) - .Take(5) - .ToList(); + private void showMetadataForBeatmap(Func getBeatmap) + { + AddStep("setup display", () => + { + var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); + + Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), Empty()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + Alpha = 0f, + }; + }); + + AddStep("fade in", () => display.FadeIn(400, Easing.OutQuint)); + AddToggleStep("trigger loading", v => display.Loading = v); + } } } From 2b90bc4f1f85e35d1610e3e1e993e97dd5df643b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:35:23 +0900 Subject: [PATCH 027/215] Remove unnecessary ruleset switching steps --- .../SongSelect/TestSceneBeatmapMetadataDisplay.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index e80c453da9..0fd9197ab4 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; using osuTK; @@ -24,17 +23,6 @@ namespace osu.Game.Tests.Visual.SongSelect [Resolved] private BeatmapManager manager { get; set; } - [Resolved] - private RulesetStore rulesets { get; set; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - foreach (var ruleset in rulesets.AvailableRulesets) - AddStep($"switch to {ruleset.Name}", () => Ruleset.Value = ruleset); - } - [Test] public void TestLocal([Values("Beatmap", "Some long title and stuff")] string title, From b7acf9de52606a8a3bc87983d5deb096a773e6ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:36:56 +0900 Subject: [PATCH 028/215] Make test work without manually clicking things --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 0fd9197ab4..49aec02aa0 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -72,10 +72,13 @@ namespace osu.Game.Tests.Visual.SongSelect Scale = new Vector2(1.5f), Alpha = 0f, }; + + display.FadeIn(400, Easing.OutQuint); }); - AddStep("fade in", () => display.FadeIn(400, Easing.OutQuint)); - AddToggleStep("trigger loading", v => display.Loading = v); + AddWaitStep("wait a bit", 5); + + AddStep("finish loading", () => display.Loading = false); } } } From 9ba412d27e9fe2161530a133efe2d74d329cdb8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 12:41:22 +0900 Subject: [PATCH 029/215] Add the osu! logo to the test scene Makes no sense to add a test intended to test visual behaviour with one of the main elements missing. Not sure how you would be able to test the flow with the logo's presence. --- .../SongSelect/TestSceneBeatmapMetadataDisplay.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 49aec02aa0..230822e070 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osuTK; @@ -65,12 +66,16 @@ namespace osu.Game.Tests.Visual.SongSelect { var randomMods = Ruleset.Value.CreateInstance().GetAllMods().OrderBy(_ => RNG.Next()).Take(5).ToList(); - Child = display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), Empty()) + OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; + + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1.5f), - Alpha = 0f, + display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + } }; display.FadeIn(400, Easing.OutQuint); From 301dab1ce8d8eb481a4180f63a7aa213919279c2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 12:55:07 +0300 Subject: [PATCH 030/215] Refactor `StarRatingDisplay` to be mutable with a current bindable --- .../Ranking/TestSceneStarRatingDisplay.cs | 15 +++- .../Ranking/Expanded/StarRatingDisplay.cs | 87 ++++++++++++------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index d0067c3396..a043f506c1 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Screens.Ranking.Expanded; @@ -10,8 +12,11 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - public TestSceneStarRatingDisplay() + [SetUp] + public void SetUp() => Schedule(() => { + StarRatingDisplay changingStarRating; + Child = new FillFlowContainer { Anchor = Anchor.Centre, @@ -25,8 +30,14 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(5.67, 0)), new StarRatingDisplay(new StarDifficulty(6.78, 0)), new StarRatingDisplay(new StarDifficulty(10.11, 0)), + changingStarRating = new StarRatingDisplay(), } }; - } + + Scheduler.AddDelayed(() => + { + changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); + }, 500, true); + }); } } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index f7e50fdc8a..a1f48fa811 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -3,12 +3,14 @@ using System.Globalization; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -20,17 +22,29 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { - private readonly StarDifficulty difficulty; + private Box background; + private OsuTextFlowContainer textFlow; + + [Resolved] + private OsuColour colours { get; set; } + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty) + public StarRatingDisplay(StarDifficulty starDifficulty = default) { - difficulty = starDifficulty; + Current.Value = starDifficulty; } [BackgroundDependencyLoader] @@ -38,15 +52,6 @@ namespace osu.Game.Screens.Ranking.Expanded { AutoSizeAxes = Axes.Both; - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - - ColourInfo backgroundColour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); - InternalChildren = new Drawable[] { new CircularContainer @@ -55,10 +60,9 @@ namespace osu.Game.Screens.Ranking.Expanded Masking = true, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour }, } }, @@ -78,32 +82,51 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, TextAnchor = Anchor.BottomLeft, - }.With(t => - { - t.AddText($"{wholePart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); - - t.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); - }) + } } } }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + } + + private void updateDisplay(StarDifficulty difficulty) + { + var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + + textFlow.Clear(); + + textFlow.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + textFlow.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); + } } } From ca772b60b183e33d0c42896f5340088c5f3206b8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 18:21:29 +0300 Subject: [PATCH 031/215] Use regular test steps rather than one-time set up and scheduling --- .../Visual/Ranking/TestSceneStarRatingDisplay.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index a043f506c1..2ff664a0d9 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -12,12 +12,12 @@ namespace osu.Game.Tests.Visual.Ranking { public class TestSceneStarRatingDisplay : OsuTestScene { - [SetUp] - public void SetUp() => Schedule(() => + [Test] + public void TestDisplay() { - StarRatingDisplay changingStarRating; + StarRatingDisplay changingStarRating = null; - Child = new FillFlowContainer + AddStep("load displays", () => Child = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -32,12 +32,12 @@ namespace osu.Game.Tests.Visual.Ranking new StarRatingDisplay(new StarDifficulty(10.11, 0)), changingStarRating = new StarRatingDisplay(), } - }; + }); - Scheduler.AddDelayed(() => + AddRepeatStep("change bottom rating", () => { changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); - }, 500, true); - }); + }, 10); + } } } From 1c49590ba2e37ca79e03f7de0599e20f8be3a2f3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 8 May 2021 21:13:18 +0300 Subject: [PATCH 032/215] Apply further refactoring to star rating display UX-wise --- .../Ranking/Expanded/StarRatingDisplay.cs | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index a1f48fa811..77c43ad863 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -22,27 +22,50 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; - private OsuTextFlowContainer textFlow; + private OsuSpriteText wholePart; + private OsuSpriteText fractionPart; + + private double displayedStarRating; + + protected double DisplayedStarRating + { + get => displayedStarRating; + set + { + displayedStarRating = value; + + var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + wholePart.Text = starRatingParts[0]; + fractionPart.Text = starRatingParts[1]; + } + } [Resolved] private OsuColour colours { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; } + /// + /// Creates a new without any set, displaying a placeholder until is changed. + /// + public StarRatingDisplay() + { + } + /// /// Creates a new using an already computed . /// /// The already computed to display the star difficulty of. - public StarRatingDisplay(StarDifficulty starDifficulty = default) + public StarRatingDisplay(StarDifficulty starDifficulty) { Current.Value = starDifficulty; } @@ -82,13 +105,40 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) + new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - TextAnchor = Anchor.BottomLeft, + Children = new[] + { + wholePart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + }, + fractionPart = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Colour = Color4.Black, + Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), + UseFullGlyphHeight = false, + } + } } } } @@ -98,35 +148,31 @@ namespace osu.Game.Screens.Ranking.Expanded protected override void LoadComplete() { base.LoadComplete(); - Current.BindValueChanged(difficulty => updateDisplay(difficulty.NewValue), true); + + Current.BindValueChanged(_ => updateDisplay(), true); + FinishTransforms(true); } - private void updateDisplay(StarDifficulty difficulty) + private void updateDisplay() { - var starRatingParts = difficulty.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - string wholePart = starRatingParts[0]; - string fractionPart = starRatingParts[1]; - string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + const double duration = 400; + const Easing easing = Easing.OutQuint; - background.Colour = difficulty.DifficultyRating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(difficulty.DifficultyRating); + ColourInfo backgroundColour; - textFlow.Clear(); - - textFlow.AddText($"{wholePart}", s => + if (Current.Value == null) + backgroundColour = Color4.SlateGray.Opacity(0.3f); + else { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 14); - s.UseFullGlyphHeight = false; - }); + var rating = Current.Value.Value.DifficultyRating; - textFlow.AddText($"{separator}{fractionPart}", s => - { - s.Colour = Color4.Black; - s.Font = s.Font.With(size: 7); - s.UseFullGlyphHeight = false; - }); + backgroundColour = rating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(rating); + } + + background.FadeColour(backgroundColour, duration, easing); + this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing); } } } From afc9a1bf235ef9c3026292254d788ce98072158d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 10 May 2021 10:16:52 +0300 Subject: [PATCH 033/215] Remove rolling support and apply few adjustments --- .../Ranking/Expanded/StarRatingDisplay.cs | 81 +++++++------------ 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 77c43ad863..422833555f 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.Containers; using osuTK; using osuTK.Graphics; @@ -25,23 +25,7 @@ namespace osu.Game.Screens.Ranking.Expanded public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; - private OsuSpriteText wholePart; - private OsuSpriteText fractionPart; - - private double displayedStarRating; - - protected double DisplayedStarRating - { - get => displayedStarRating; - set - { - displayedStarRating = value; - - var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); - wholePart.Text = starRatingParts[0]; - fractionPart.Text = starRatingParts[1]; - } - } + private OsuTextFlowContainer textFlow; [Resolved] private OsuColour colours { get; set; } @@ -105,40 +89,13 @@ namespace osu.Game.Screens.Ranking.Expanded Icon = FontAwesome.Solid.Star, Colour = Color4.Black }, - new FillFlowContainer + textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black)) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Children = new[] - { - wholePart = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black), - UseFullGlyphHeight = false, - }, - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, - Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), - UseFullGlyphHeight = false, - }, - fractionPart = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = Color4.Black, - Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black), - UseFullGlyphHeight = false, - } - } + TextAnchor = Anchor.BottomLeft, } } } @@ -158,21 +115,39 @@ namespace osu.Game.Screens.Ranking.Expanded const double duration = 400; const Easing easing = Easing.OutQuint; - ColourInfo backgroundColour; + double stars = Current.Value?.Stars ?? 0.00f; + + var starRatingParts = stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + string wholePart = starRatingParts[0]; + string fractionPart = starRatingParts[1]; + string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; if (Current.Value == null) - backgroundColour = Color4.SlateGray.Opacity(0.3f); + background.FadeColour(Color4.SlateGray.Opacity(0.3f)); else { var rating = Current.Value.Value.DifficultyRating; - backgroundColour = rating == DifficultyRating.ExpertPlus + background.FadeColour(rating == DifficultyRating.ExpertPlus ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(rating); + : (ColourInfo)colours.ForDifficultyRating(rating), duration, easing); } - background.FadeColour(backgroundColour, duration, easing); - this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing); + textFlow.Clear(); + + textFlow.AddText($"{wholePart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 14); + s.UseFullGlyphHeight = false; + }); + + textFlow.AddText($"{separator}{fractionPart}", s => + { + s.Colour = Color4.Black; + s.Font = s.Font.With(size: 7); + s.UseFullGlyphHeight = false; + }); } } } From b9ab9342faeee26c54903beb326109698c8525b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 May 2021 15:16:16 +0900 Subject: [PATCH 034/215] Setup basics to allow extracting serializable content from skinnable `Drawable`s --- osu.Game/Extensions/DrawableExtensions.cs | 3 ++ osu.Game/Screens/Play/HUD/ISkinnableInfo.cs | 32 ++++++++++++++ .../HUD/SkinnableElementTargetContainer.cs | 11 +++++ .../Screens/Play/HUD/StoredSkinnableInfo.cs | 43 +++++++++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 38 +++++++--------- .../Skinning/Editor/SkinBlueprintContainer.cs | 13 +++++- osu.Game/Skinning/ISkin.cs | 2 + 7 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ISkinnableInfo.cs create mode 100644 osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs create mode 100644 osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index a8de3f6407..3d2ffe0ea1 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Framework.Threading; +using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Extensions @@ -43,5 +44,7 @@ namespace osu.Game.Extensions /// The delta vector in Parent's coordinates. public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) => drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta); + + public static StoredSkinnableInfo CreateSerialisedInformation(this Drawable component) => new StoredSkinnableInfo(component); } } diff --git a/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs b/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs new file mode 100644 index 0000000000..0e571b5cb4 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs @@ -0,0 +1,32 @@ +// 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.Graphics; +using osu.Game.IO.Serialization; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + public interface ISkinnableInfo : IJsonSerializable + { + public Type Type { get; set; } + + public Type Target { get; set; } + + public Vector2 Position { get; set; } + + public float Rotation { get; set; } + + public Vector2 Scale { get; set; } + + public Anchor Anchor { get; set; } + } + + /// + /// A container which supports skinnable components being added to it. + /// + public interface ISkinnableTarget + { + } +} diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs new file mode 100644 index 0000000000..7ba32e2d46 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -0,0 +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 osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Play.HUD +{ + public class SkinnableElementTargetContainer : Container, ISkinnableTarget + { + } +} diff --git a/osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs b/osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs new file mode 100644 index 0000000000..e4ec035e68 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs @@ -0,0 +1,43 @@ +// 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.Graphics; +using osuTK; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// Serialised information governing custom changes to an . + /// + public class StoredSkinnableInfo : ISkinnableInfo + { + public StoredSkinnableInfo(Drawable component) + { + Type = component.GetType(); + + var target = component.Parent as ISkinnableTarget + // todo: this is temporary until we serialise the default layouts out of SkinnableDrawables. + ?? component.Parent?.Parent as ISkinnableTarget; + + Target = target?.GetType(); + + Position = component.Position; + Rotation = component.Rotation; + Scale = component.Scale; + Anchor = component.Anchor; + } + + public Type Type { get; set; } + + public Type Target { get; set; } + + public Vector2 Position { get; set; } + + public float Rotation { get; set; } + + public Vector2 Scale { get; set; } + + public Anchor Anchor { get; set; } + } +} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 6ddaa338cc..13449e3a2b 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -91,16 +91,16 @@ namespace osu.Game.Screens.Play { new Drawable[] { - new Container + new MainHUDElements { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - HealthDisplay = CreateHealthDisplay(), - AccuracyCounter = CreateAccuracyCounter(), - ScoreCounter = CreateScoreCounter(), - CreateComboCounter(), - CreateHitErrorDisplayOverlay(), + HealthDisplay = new SkinnableHealthDisplay(), + AccuracyCounter = new SkinnableAccuracyCounter(), + ScoreCounter = new SkinnableScoreCounter(), + new SkinnableComboCounter(), + new HitErrorDisplay(this.drawableRuleset?.FirstAvailableHitWindows), } }, }, @@ -263,48 +263,38 @@ namespace osu.Game.Screens.Play Progress.BindDrawableRuleset(drawableRuleset); } - protected SkinnableAccuracyCounter CreateAccuracyCounter() => new SkinnableAccuracyCounter(); - - protected SkinnableScoreCounter CreateScoreCounter() => new SkinnableScoreCounter(); - - protected SkinnableComboCounter CreateComboCounter() => new SkinnableComboCounter(); - - protected SkinnableHealthDisplay CreateHealthDisplay() => new SkinnableHealthDisplay(); - - protected virtual FailingLayer CreateFailingLayer() => new FailingLayer + protected FailingLayer CreateFailingLayer() => new FailingLayer { ShowHealth = { BindTarget = ShowHealthbar } }; - protected virtual KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay + protected KeyCounterDisplay CreateKeyCounter() => new KeyCounterDisplay { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, }; - protected virtual SongProgress CreateProgress() => new SongProgress + protected SongProgress CreateProgress() => new SongProgress { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, }; - protected virtual HoldForMenuButton CreateHoldForMenuButton() => new HoldForMenuButton + protected HoldForMenuButton CreateHoldForMenuButton() => new HoldForMenuButton { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, }; - protected virtual ModDisplay CreateModsContainer() => new ModDisplay + protected ModDisplay CreateModsContainer() => new ModDisplay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, AutoSizeAxes = Axes.Both, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(drawableRuleset?.FirstAvailableHitWindows); - - protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); + protected PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); public bool OnPressed(GlobalAction action) { @@ -347,5 +337,9 @@ namespace osu.Game.Screens.Play break; } } + + public class MainHUDElements : SkinnableElementTargetContainer + { + } } } diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 35e93d9aff..a6f60afd90 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -1,9 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.IO; using System.Linq; +using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Testing; +using osu.Game.Extensions; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components; @@ -27,7 +30,15 @@ namespace osu.Game.Skinning.Editor private void checkForComponents() { - foreach (var c in target.ChildrenOfType().ToArray()) AddBlueprintFor(c); + ISkinnableComponent[] skinnableComponents = target.ChildrenOfType().ToArray(); + + // todo: the OfType() call can be removed with better IDrawable support. + string json = JsonConvert.SerializeObject(skinnableComponents.OfType().Select(d => d.CreateSerialisedInformation()), new JsonSerializerSettings { Formatting = Formatting.Indented }); + + File.WriteAllText("/Users/Dean/json-out.json", json); + + foreach (var c in skinnableComponents) + AddBlueprintFor(c); // We'd hope to eventually be running this in a more sensible way, but this handles situations where new drawables become present (ie. during ongoing gameplay) // or when drawables in the target are loaded asynchronously and may not be immediately available when this BlueprintContainer is loaded. diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 73f7cf6d39..9fa781fa5d 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -57,5 +57,7 @@ namespace osu.Game.Skinning /// A matching value boxed in an , or null if unavailable. [CanBeNull] IBindable GetConfig(TLookup lookup); + + // IEnumerable ComponentInfo { get; } } } From 67ea4a7e97bce85d0f5f861d021b7113809bc16b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 18:18:29 +0900 Subject: [PATCH 035/215] Read from skin config --- .../Play/HUD/SkinnableElementTargetContainer.cs | 17 +++++++++++++++-- osu.Game/Skinning/ISkin.cs | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index 7ba32e2d46..8da7950c57 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -1,11 +1,24 @@ // 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.Graphics.Containers; +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class SkinnableElementTargetContainer : Container, ISkinnableTarget + public class SkinnableElementTargetContainer : SkinReloadableDrawable, ISkinnableTarget { + protected override void SkinChanged(ISkinSource skin, bool allowFallback) + { + base.SkinChanged(skin, allowFallback); + + var loadable = skin.GetConfig>(this.GetType()); + + ClearInternal(); + if (loadable != null) + LoadComponentsAsync(loadable.Value, AddRangeInternal); + } } } diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 9fa781fa5d..5371893882 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,6 +1,8 @@ // 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 JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -57,7 +59,5 @@ namespace osu.Game.Skinning /// A matching value boxed in an , or null if unavailable. [CanBeNull] IBindable GetConfig(TLookup lookup); - - // IEnumerable ComponentInfo { get; } } } From 95a8f21ab26065f1bccc4d8cfa2ad83051da27a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 May 2021 19:13:38 +0900 Subject: [PATCH 036/215] Add the concept of skinnable target containers and mark a basic one for HUD elements --- osu.Game/Extensions/DrawableExtensions.cs | 11 +++++- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 3 +- osu.Game/Screens/Play/HUD/ISkinnableInfo.cs | 10 +---- osu.Game/Screens/Play/HUD/ISkinnableTarget.cs | 15 +++++++ .../HUD/SkinnableElementTargetContainer.cs | 14 ++++--- ...toredSkinnableInfo.cs => SkinnableInfo.cs} | 32 +++++++++++---- osu.Game/Screens/Play/HUDOverlay.cs | 37 +++++++++++++----- osu.Game/Skinning/DefaultSkin.cs | 39 ++++++++++++++++++- osu.Game/Skinning/ISkin.cs | 2 - osu.Game/Skinning/SkinnableTarget.cs | 10 +++++ osu.Game/Skinning/SkinnableTargetComponent.cs | 17 ++++++++ 11 files changed, 154 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ISkinnableTarget.cs rename osu.Game/Screens/Play/HUD/{StoredSkinnableInfo.cs => SkinnableInfo.cs} (51%) create mode 100644 osu.Game/Skinning/SkinnableTarget.cs create mode 100644 osu.Game/Skinning/SkinnableTargetComponent.cs diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 3d2ffe0ea1..289b305c27 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -45,6 +45,15 @@ namespace osu.Game.Extensions public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) => drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta); - public static StoredSkinnableInfo CreateSerialisedInformation(this Drawable component) => new StoredSkinnableInfo(component); + public static SkinnableInfo CreateSerialisedInformation(this Drawable component) => new SkinnableInfo(component); + + public static void ApplySerialisedInformation(this Drawable component, ISkinnableInfo info) + { + // todo: can probably make this better via deserialisation directly using a common interface. + component.Position = info.Position; + component.Rotation = info.Rotation; + component.Scale = info.Scale; + component.Anchor = info.Anchor; + } } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 2e84c9c97d..0e147f9238 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -13,13 +13,12 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter, ISkinnableComponent + public class BarHitErrorMeter : HitErrorMeter { private readonly Anchor alignment; diff --git a/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs b/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs index 0e571b5cb4..fc4e9680a8 100644 --- a/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Game.IO.Serialization; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -12,7 +13,7 @@ namespace osu.Game.Screens.Play.HUD { public Type Type { get; set; } - public Type Target { get; set; } + public SkinnableTarget? Target { get; set; } public Vector2 Position { get; set; } @@ -22,11 +23,4 @@ namespace osu.Game.Screens.Play.HUD public Anchor Anchor { get; set; } } - - /// - /// A container which supports skinnable components being added to it. - /// - public interface ISkinnableTarget - { - } } diff --git a/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs b/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs new file mode 100644 index 0000000000..6cd269333a --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// A container which supports skinnable components being added to it. + /// + public interface ISkinnableTarget + { + public SkinnableTarget Target { get; } + } +} diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index 8da7950c57..bd82533823 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -1,24 +1,28 @@ // 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 osu.Framework.Graphics; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { public class SkinnableElementTargetContainer : SkinReloadableDrawable, ISkinnableTarget { + public SkinnableTarget Target { get; } + + public SkinnableElementTargetContainer(SkinnableTarget target) + { + Target = target; + } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); - var loadable = skin.GetConfig>(this.GetType()); + var loadable = skin.GetDrawableComponent(new SkinnableTargetComponent(Target)); ClearInternal(); if (loadable != null) - LoadComponentsAsync(loadable.Value, AddRangeInternal); + LoadComponentAsync(loadable, AddInternal); } } } diff --git a/osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs similarity index 51% rename from osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs rename to osu.Game/Screens/Play/HUD/SkinnableInfo.cs index e4ec035e68..5b1f840efd 100644 --- a/osu.Game/Screens/Play/HUD/StoredSkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -2,7 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Framework.Graphics; +using osu.Game.Extensions; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Screens.Play.HUD @@ -10,17 +14,20 @@ namespace osu.Game.Screens.Play.HUD /// /// Serialised information governing custom changes to an . /// - public class StoredSkinnableInfo : ISkinnableInfo + [Serializable] + public class SkinnableInfo : ISkinnableInfo { - public StoredSkinnableInfo(Drawable component) + public SkinnableInfo() + { + } + + public SkinnableInfo(Drawable component) { Type = component.GetType(); - var target = component.Parent as ISkinnableTarget - // todo: this is temporary until we serialise the default layouts out of SkinnableDrawables. - ?? component.Parent?.Parent as ISkinnableTarget; + ISkinnableTarget target = component.Parent as ISkinnableTarget; - Target = target?.GetType(); + Target = target?.Target; Position = component.Position; Rotation = component.Rotation; @@ -30,7 +37,8 @@ namespace osu.Game.Screens.Play.HUD public Type Type { get; set; } - public Type Target { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + public SkinnableTarget? Target { get; set; } public Vector2 Position { get; set; } @@ -40,4 +48,14 @@ namespace osu.Game.Screens.Play.HUD public Anchor Anchor { get; set; } } + + public static class SkinnableInfoExtensions + { + public static Drawable CreateInstance(this ISkinnableInfo info) + { + Drawable d = (Drawable)Activator.CreateInstance(info.Type); + d.ApplySerialisedInformation(info); + return d; + } + } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 13449e3a2b..1a12ca4cbd 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -91,16 +91,37 @@ namespace osu.Game.Screens.Play { new Drawable[] { - new MainHUDElements + new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - HealthDisplay = new SkinnableHealthDisplay(), - AccuracyCounter = new SkinnableAccuracyCounter(), - ScoreCounter = new SkinnableScoreCounter(), - new SkinnableComboCounter(), - new HitErrorDisplay(this.drawableRuleset?.FirstAvailableHitWindows), + new SkinnableElementTargetContainer(SkinnableTarget.MainHUDComponents) + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Children = new Drawable[] + { + // remaining cross-dependencies need tidying. + // kept to ensure non-null, but hidden for testing. + HealthDisplay = new SkinnableHealthDisplay(), + AccuracyCounter = new SkinnableAccuracyCounter(), + ScoreCounter = new SkinnableScoreCounter(), + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + // still need to be migrated; a bit more involved. + new HitErrorDisplay(this.drawableRuleset?.FirstAvailableHitWindows), + } + }, } }, }, @@ -337,9 +358,5 @@ namespace osu.Game.Screens.Play break; } } - - public class MainHUDElements : SkinnableElementTargetContainer - { - } } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 0b3f5f3cde..48da841c45 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -2,12 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.Screens.Play.HUD; using osuTK.Graphics; namespace osu.Game.Skinning @@ -20,7 +25,29 @@ namespace osu.Game.Skinning Configuration = new DefaultSkinConfiguration(); } - public override Drawable GetDrawableComponent(ISkinComponent component) => null; + public override Drawable GetDrawableComponent(ISkinComponent component) + { + switch (component) + { + case SkinnableTargetComponent target: + switch (target.Target) + { + case SkinnableTarget.MainHUDComponents: + var infos = JsonConvert.DeserializeObject>(File.ReadAllText("/Users/Dean/json-out.json")).Where(i => i.Target == SkinnableTarget.MainHUDComponents); + + var container = new SkinnableTargetWrapper(target.Target) + { + ChildrenEnumerable = infos.Select(i => i.CreateInstance()) + }; + + return container; + } + + break; + } + + return null; + } public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; @@ -45,4 +72,14 @@ namespace osu.Game.Skinning return null; } } + + public class SkinnableTargetWrapper : Container, ISkinnableTarget + { + public SkinnableTarget Target { get; } + + public SkinnableTargetWrapper(SkinnableTarget target) + { + Target = target; + } + } } diff --git a/osu.Game/Skinning/ISkin.cs b/osu.Game/Skinning/ISkin.cs index 5371893882..73f7cf6d39 100644 --- a/osu.Game/Skinning/ISkin.cs +++ b/osu.Game/Skinning/ISkin.cs @@ -1,8 +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 System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; diff --git a/osu.Game/Skinning/SkinnableTarget.cs b/osu.Game/Skinning/SkinnableTarget.cs new file mode 100644 index 0000000000..7b1eae126c --- /dev/null +++ b/osu.Game/Skinning/SkinnableTarget.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public enum SkinnableTarget + { + MainHUDComponents + } +} diff --git a/osu.Game/Skinning/SkinnableTargetComponent.cs b/osu.Game/Skinning/SkinnableTargetComponent.cs new file mode 100644 index 0000000000..a17aafe6e7 --- /dev/null +++ b/osu.Game/Skinning/SkinnableTargetComponent.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Skinning +{ + public class SkinnableTargetComponent : ISkinComponent + { + public readonly SkinnableTarget Target; + + public string LookupName => Target.ToString(); + + public SkinnableTargetComponent(SkinnableTarget target) + { + Target = target; + } + } +} From 95a497e9df7313ed1e6ae0513c1a623fba63c7cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 15:31:14 +0900 Subject: [PATCH 037/215] Remove unused interface class for simplicity --- osu.Game/Extensions/DrawableExtensions.cs | 2 +- osu.Game/Screens/Play/HUD/ISkinnableInfo.cs | 26 --------------------- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 12 ++++------ 3 files changed, 6 insertions(+), 34 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/ISkinnableInfo.cs diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 289b305c27..e84c9e5a98 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -47,7 +47,7 @@ namespace osu.Game.Extensions public static SkinnableInfo CreateSerialisedInformation(this Drawable component) => new SkinnableInfo(component); - public static void ApplySerialisedInformation(this Drawable component, ISkinnableInfo info) + public static void ApplySerialisedInformation(this Drawable component, SkinnableInfo info) { // todo: can probably make this better via deserialisation directly using a common interface. component.Position = info.Position; diff --git a/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs b/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs deleted file mode 100644 index fc4e9680a8..0000000000 --- a/osu.Game/Screens/Play/HUD/ISkinnableInfo.cs +++ /dev/null @@ -1,26 +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.Graphics; -using osu.Game.IO.Serialization; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Screens.Play.HUD -{ - public interface ISkinnableInfo : IJsonSerializable - { - public Type Type { get; set; } - - public SkinnableTarget? Target { get; set; } - - public Vector2 Position { get; set; } - - public float Rotation { get; set; } - - public Vector2 Scale { get; set; } - - public Anchor Anchor { get; set; } - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 5b1f840efd..2edc99781f 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -6,6 +6,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Framework.Graphics; using osu.Game.Extensions; +using osu.Game.IO.Serialization; using osu.Game.Skinning; using osuTK; @@ -15,7 +16,7 @@ namespace osu.Game.Screens.Play.HUD /// Serialised information governing custom changes to an . /// [Serializable] - public class SkinnableInfo : ISkinnableInfo + public class SkinnableInfo : IJsonSerializable { public SkinnableInfo() { @@ -47,14 +48,11 @@ namespace osu.Game.Screens.Play.HUD public Vector2 Scale { get; set; } public Anchor Anchor { get; set; } - } - public static class SkinnableInfoExtensions - { - public static Drawable CreateInstance(this ISkinnableInfo info) + public Drawable CreateInstance() { - Drawable d = (Drawable)Activator.CreateInstance(info.Type); - d.ApplySerialisedInformation(info); + Drawable d = (Drawable)Activator.CreateInstance(Type); + d.ApplySerialisedInformation(this); return d; } } From df72656aa1238a337550ed1b114a45300c2455a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 15:40:33 +0900 Subject: [PATCH 038/215] Remove downwards dependency from `HUDOverlay` to `HealthDisplay` --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 13 +++++++++++-- osu.Game/Screens/Play/HUDOverlay.cs | 3 --- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 6c2571cc28..e0ddde026b 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -16,6 +17,8 @@ namespace osu.Game.Screens.Play.HUD /// public abstract class HealthDisplay : Container { + private Bindable showHealthbar; + [Resolved] protected HealthProcessor HealthProcessor { get; private set; } @@ -29,12 +32,18 @@ namespace osu.Game.Screens.Play.HUD { } - [BackgroundDependencyLoader] - private void load() + [BackgroundDependencyLoader(true)] + private void load(HUDOverlay hud) { Current.BindTo(HealthProcessor.Health); HealthProcessor.NewJudgement += onNewJudgement; + + if (hud != null) + { + showHealthbar = hud.ShowHealthbar.GetBoundCopy(); + showHealthbar.BindValueChanged(healthBar => this.FadeTo(healthBar.NewValue ? 1 : 0, HUDOverlay.FADE_DURATION, HUDOverlay.FADE_EASING), true); + } } private void onNewJudgement(JudgementResult judgement) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 1a12ca4cbd..7220365673 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -36,7 +36,6 @@ namespace osu.Game.Screens.Play public readonly KeyCounterDisplay KeyCounter; public readonly SkinnableScoreCounter ScoreCounter; public readonly SkinnableAccuracyCounter AccuracyCounter; - public readonly SkinnableHealthDisplay HealthDisplay; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; @@ -108,7 +107,6 @@ namespace osu.Game.Screens.Play { // remaining cross-dependencies need tidying. // kept to ensure non-null, but hidden for testing. - HealthDisplay = new SkinnableHealthDisplay(), AccuracyCounter = new SkinnableAccuracyCounter(), ScoreCounter = new SkinnableScoreCounter(), } @@ -206,7 +204,6 @@ namespace osu.Game.Screens.Play { base.LoadComplete(); - ShowHealthbar.BindValueChanged(healthBar => HealthDisplay.FadeTo(healthBar.NewValue ? 1 : 0, FADE_DURATION, FADE_EASING), true); ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, FADE_DURATION, FADE_EASING))); IsBreakTime.BindValueChanged(_ => updateVisibility()); From 1742ee89e0680662cd5d12348ddceef4c5df2be9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 22:32:56 +0900 Subject: [PATCH 039/215] Fix incorrect xmldoc for `DeleteFile` --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index dbeaebb1cd..e0f80d2743 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -472,7 +472,7 @@ namespace osu.Game.Database } /// - /// Delete new file. + /// Delete an existing file. /// /// The item to operate on. /// The existing file to be deleted. From 6a88b8888b1be641d203ed598998b7f0e985c410 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 22:33:45 +0900 Subject: [PATCH 040/215] Add basic support for child serialisation --- osu.Game/Extensions/DrawableExtensions.cs | 7 ++++ osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 37 ++++++++++++++-------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index e84c9e5a98..6837a802f2 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Threading; using osu.Game.Screens.Play.HUD; @@ -54,6 +55,12 @@ namespace osu.Game.Extensions component.Rotation = info.Rotation; component.Scale = info.Scale; component.Anchor = info.Anchor; + + if (component is Container container) + { + foreach (var child in info.Children) + container.Add(child.CreateInstance()); + } } } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 2edc99781f..ec4eaa0e59 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -2,9 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Extensions; using osu.Game.IO.Serialization; using osu.Game.Skinning; @@ -18,6 +21,21 @@ namespace osu.Game.Screens.Play.HUD [Serializable] public class SkinnableInfo : IJsonSerializable { + public Type Type { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public SkinnableTarget? Target { get; set; } + + public Vector2 Position { get; set; } + + public float Rotation { get; set; } + + public Vector2 Scale { get; set; } + + public Anchor Anchor { get; set; } + + public List Children { get; } = new List(); + public SkinnableInfo() { } @@ -34,21 +52,14 @@ namespace osu.Game.Screens.Play.HUD Rotation = component.Rotation; Scale = component.Scale; Anchor = component.Anchor; + + if (component is Container container) + { + foreach (var child in container.Children.OfType().OfType()) + Children.Add(child.CreateSerialisedInformation()); + } } - public Type Type { get; set; } - - [JsonConverter(typeof(StringEnumConverter))] - public SkinnableTarget? Target { get; set; } - - public Vector2 Position { get; set; } - - public float Rotation { get; set; } - - public Vector2 Scale { get; set; } - - public Anchor Anchor { get; set; } - public Drawable CreateInstance() { Drawable d = (Drawable)Activator.CreateInstance(Type); From b54eb56169e08757e85abe78b1113e7425108717 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 22:34:24 +0900 Subject: [PATCH 041/215] Move new judgement binding to `LoadComplete` to ensure containers are loaded --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index e0ddde026b..e18a28eded 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -37,8 +37,6 @@ namespace osu.Game.Screens.Play.HUD { Current.BindTo(HealthProcessor.Health); - HealthProcessor.NewJudgement += onNewJudgement; - if (hud != null) { showHealthbar = hud.ShowHealthbar.GetBoundCopy(); @@ -46,6 +44,13 @@ namespace osu.Game.Screens.Play.HUD } } + protected override void LoadComplete() + { + base.LoadComplete(); + + HealthProcessor.NewJudgement += onNewJudgement; + } + private void onNewJudgement(JudgementResult judgement) { if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) From 004798d61d44d88114acfd7c2861378261eab107 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 22:36:20 +0900 Subject: [PATCH 042/215] Update Legacy components to not require skin in ctor --- .../Legacy/LegacyCatchComboCounter.cs | 4 ++-- .../Skinning/Legacy/LegacySpinner.cs | 4 ++-- .../Legacy/OsuLegacySkinTransformer.cs | 2 +- .../Visual/Gameplay/TestSceneSkinEditor.cs | 14 ++++++++++++- .../Screens/Play/HUD/LegacyComboCounter.cs | 4 ++-- .../HUD/SkinnableElementTargetContainer.cs | 15 ++++++++++--- osu.Game/Skinning/LegacyAccuracyCounter.cs | 11 +++++----- osu.Game/Skinning/LegacyHealthDisplay.cs | 21 ++++++++----------- osu.Game/Skinning/LegacyRollingCounter.cs | 7 ++----- osu.Game/Skinning/LegacyScoreCounter.cs | 12 +++++------ osu.Game/Skinning/LegacySkin.cs | 6 +++--- osu.Game/Skinning/LegacySpriteText.cs | 14 ++++++++++--- 12 files changed, 68 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs index 28ee7bd813..33c3867f5a 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatchComboCounter.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy InternalChildren = new Drawable[] { - explosion = new LegacyRollingCounter(skin, LegacyFont.Combo) + explosion = new LegacyRollingCounter(LegacyFont.Combo) { Alpha = 0.65f, Blending = BlendingParameters.Additive, @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy Origin = Anchor.Centre, Scale = new Vector2(1.5f), }, - counter = new LegacyRollingCounter(skin, LegacyFont.Combo) + counter = new LegacyRollingCounter(LegacyFont.Combo) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index 7eb6898abc..959589620b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Scale = new Vector2(SPRITE_SCALE), Y = SPINNER_TOP_OFFSET + 115, }, - bonusCounter = new LegacySpriteText(source, LegacyFont.Score) + bonusCounter = new LegacySpriteText(LegacyFont.Score) { Alpha = 0f, Anchor = Anchor.TopCentre, @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Scale = new Vector2(SPRITE_SCALE), Position = new Vector2(-87, 445 + spm_hide_offset), }, - spmCounter = new LegacySpriteText(source, LegacyFont.Score) + spmCounter = new LegacySpriteText(LegacyFont.Score) { Anchor = Anchor.TopCentre, Origin = Anchor.TopRight, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index ffe238c507..88302ebc57 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (!this.HasFont(LegacyFont.HitCircle)) return null; - return new LegacySpriteText(Source, LegacyFont.HitCircle) + return new LegacySpriteText(LegacyFont.HitCircle) { // stable applies a blanket 0.8x scale to hitcircle fonts Scale = new Vector2(0.8f), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index c53ac42d12..b0edc0dd68 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Skinning; using osu.Game.Skinning.Editor; namespace osu.Game.Tests.Visual.Gameplay @@ -14,12 +16,22 @@ namespace osu.Game.Tests.Visual.Gameplay { private SkinEditor skinEditor; + [Resolved] + private SkinManager skinManager { get; set; } + [SetUpSteps] public override void SetUpSteps() { + AddStep("set empty legacy skin", () => + { + var imported = skinManager.Import(new SkinInfo { Name = "test skin" }).Result; + + skinManager.CurrentSkinInfo.Value = imported; + }); + base.SetUpSteps(); - AddStep("add editor overlay", () => + AddStep("reload skin editor", () => { skinEditor?.Expire(); Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE); diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 73305ac93e..2565faf423 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -84,13 +84,13 @@ namespace osu.Game.Screens.Play.HUD { InternalChildren = new[] { - popOutCount = new LegacySpriteText(skin, LegacyFont.Combo) + popOutCount = new LegacySpriteText(LegacyFont.Combo) { Alpha = 0, Margin = new MarginPadding(0.05f), Blending = BlendingParameters.Additive, }, - displayedCountSpriteText = new LegacySpriteText(skin, LegacyFont.Combo) + displayedCountSpriteText = new LegacySpriteText(LegacyFont.Combo) { Alpha = 0, }, diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index bd82533823..f1b282119b 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -1,12 +1,17 @@ // 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 osu.Game.Extensions; using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { public class SkinnableElementTargetContainer : SkinReloadableDrawable, ISkinnableTarget { + private SkinnableTargetWrapper content; + public SkinnableTarget Target { get; } public SkinnableElementTargetContainer(SkinnableTarget target) @@ -14,15 +19,19 @@ namespace osu.Game.Screens.Play.HUD Target = target; } + public IEnumerable CreateSerialisedChildren() => + content.Select(d => d.CreateSerialisedInformation()); + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); - var loadable = skin.GetDrawableComponent(new SkinnableTargetComponent(Target)); + content = skin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; ClearInternal(); - if (loadable != null) - LoadComponentAsync(loadable, AddInternal); + + if (content != null) + LoadComponentAsync(content, AddInternal); } } } diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index 3efcd5555e..e2fe01efe4 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -12,23 +12,22 @@ namespace osu.Game.Skinning { public class LegacyAccuracyCounter : GameplayAccuracyCounter, ISkinnableComponent { - private readonly ISkin skin; + [Resolved] + private ISkinSource skin { get; set; } - public LegacyAccuracyCounter(ISkin skin) + public LegacyAccuracyCounter() { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; Scale = new Vector2(0.6f); Margin = new MarginPadding(10); - - this.skin = skin; } [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } - protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, LegacyFont.Score) + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -38,7 +37,7 @@ namespace osu.Game.Skinning { base.Update(); - if (hud?.ScoreCounter.Drawable is LegacyScoreCounter score) + if (hud?.ScoreCounter?.Drawable is LegacyScoreCounter score) { // for now align with the score counter. eventually this will be user customisable. Y = Parent.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index c1979efbc2..717ca62da7 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -20,7 +20,9 @@ namespace osu.Game.Skinning { private const double epic_cutoff = 0.5; - private readonly Skin skin; + [Resolved] + private ISkinSource skin { get; set; } + private LegacyHealthPiece fill; private LegacyHealthPiece marker; @@ -28,11 +30,6 @@ namespace osu.Game.Skinning private bool isNewStyle; - public LegacyHealthDisplay(Skin skin) - { - this.skin = skin; - } - [BackgroundDependencyLoader] private void load() { @@ -79,7 +76,7 @@ namespace osu.Game.Skinning protected override void Flash(JudgementResult result) => marker.Flash(result); - private static Texture getTexture(Skin skin, string name) => skin.GetTexture($"scorebar-{name}"); + private static Texture getTexture(ISkinSource skin, string name) => skin.GetTexture($"scorebar-{name}"); private static Color4 getFillColour(double hp) { @@ -98,7 +95,7 @@ namespace osu.Game.Skinning private readonly Texture dangerTexture; private readonly Texture superDangerTexture; - public LegacyOldStyleMarker(Skin skin) + public LegacyOldStyleMarker(ISkinSource skin) { normalTexture = getTexture(skin, "ki"); dangerTexture = getTexture(skin, "kidanger"); @@ -129,9 +126,9 @@ namespace osu.Game.Skinning public class LegacyNewStyleMarker : LegacyMarker { - private readonly Skin skin; + private readonly ISkinSource skin; - public LegacyNewStyleMarker(Skin skin) + public LegacyNewStyleMarker(ISkinSource skin) { this.skin = skin; } @@ -153,7 +150,7 @@ namespace osu.Game.Skinning internal class LegacyOldStyleFill : LegacyHealthPiece { - public LegacyOldStyleFill(Skin skin) + public LegacyOldStyleFill(ISkinSource skin) { // required for sizing correctly.. var firstFrame = getTexture(skin, "colour-0"); @@ -176,7 +173,7 @@ namespace osu.Game.Skinning internal class LegacyNewStyleFill : LegacyHealthPiece { - public LegacyNewStyleFill(Skin skin) + public LegacyNewStyleFill(ISkinSource skin) { InternalChild = new Sprite { diff --git a/osu.Game/Skinning/LegacyRollingCounter.cs b/osu.Game/Skinning/LegacyRollingCounter.cs index 0261db0e64..b531ae1e6f 100644 --- a/osu.Game/Skinning/LegacyRollingCounter.cs +++ b/osu.Game/Skinning/LegacyRollingCounter.cs @@ -12,7 +12,6 @@ namespace osu.Game.Skinning /// public class LegacyRollingCounter : RollingCounter { - private readonly ISkin skin; private readonly LegacyFont font; protected override bool IsRollingProportional => true; @@ -20,11 +19,9 @@ namespace osu.Game.Skinning /// /// Creates a new . /// - /// The from which to get counter number sprites. /// The legacy font to use for the counter. - public LegacyRollingCounter(ISkin skin, LegacyFont font) + public LegacyRollingCounter(LegacyFont font) { - this.skin = skin; this.font = font; } @@ -33,6 +30,6 @@ namespace osu.Game.Skinning return Math.Abs(newValue - currentValue) * 75.0; } - protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, font); + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(font); } } diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index ecb907e601..0696d2bedd 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Play.HUD; @@ -10,24 +11,23 @@ namespace osu.Game.Skinning { public class LegacyScoreCounter : GameplayScoreCounter, ISkinnableComponent { - private readonly ISkin skin; - protected override double RollingDuration => 1000; protected override Easing RollingEasing => Easing.Out; - public LegacyScoreCounter(ISkin skin) + [Resolved] + private ISkinSource skin { get; set; } + + public LegacyScoreCounter() : base(6) { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; - this.skin = skin; - Scale = new Vector2(0.96f); Margin = new MarginPadding(10); } - protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(skin, LegacyFont.Score) + protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index eae3b69233..70f8d1d882 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -334,13 +334,13 @@ namespace osu.Game.Skinning return new LegacyComboCounter(); case HUDSkinComponents.ScoreCounter: - return new LegacyScoreCounter(this); + return new LegacyScoreCounter(); case HUDSkinComponents.AccuracyCounter: - return new LegacyAccuracyCounter(this); + return new LegacyAccuracyCounter(); case HUDSkinComponents.HealthDisplay: - return new LegacyHealthDisplay(this); + return new LegacyHealthDisplay(); } return null; diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index c55400e219..7895fcccca 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Text; using osu.Game.Graphics.Sprites; @@ -9,19 +10,26 @@ using osuTK; namespace osu.Game.Skinning { - public class LegacySpriteText : OsuSpriteText + public sealed class LegacySpriteText : OsuSpriteText { - private readonly LegacyGlyphStore glyphStore; + private readonly LegacyFont font; + + private LegacyGlyphStore glyphStore; protected override char FixedWidthReferenceCharacter => '5'; protected override char[] FixedWidthExcludeCharacters => new[] { ',', '.', '%', 'x' }; - public LegacySpriteText(ISkin skin, LegacyFont font) + public LegacySpriteText(LegacyFont font) { + this.font = font; Shadow = false; UseFullGlyphHeight = false; + } + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { Font = new FontUsage(skin.GetFontPrefix(font), 1, fixedWidth: true); Spacing = new Vector2(-skin.GetFontOverlap(font), 0); From b248b2e5e3bf8b62ea94b9e1d5eb85e3809d448e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 May 2021 22:43:48 +0900 Subject: [PATCH 043/215] Hook up full save/load flow --- osu.Game/Screens/Play/HUD/ISkinnableTarget.cs | 15 ---- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 42 +--------- .../Skinning/Editor/SkinBlueprintContainer.cs | 8 -- osu.Game/Skinning/Editor/SkinEditor.cs | 43 +++++++++- osu.Game/Skinning/ISkinnableTarget.cs | 4 +- osu.Game/Skinning/LegacySkin.cs | 26 +++++- osu.Game/Skinning/Skin.cs | 81 ++++++++++++++++++- osu.Game/Skinning/SkinManager.cs | 32 ++++++++ 9 files changed, 181 insertions(+), 72 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/ISkinnableTarget.cs diff --git a/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs b/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs deleted file mode 100644 index 6cd269333a..0000000000 --- a/osu.Game/Screens/Play/HUD/ISkinnableTarget.cs +++ /dev/null @@ -1,15 +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.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - /// - /// A container which supports skinnable components being added to it. - /// - public interface ISkinnableTarget - { - public SkinnableTarget Target { get; } - } -} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 7220365673..30b735d28a 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -22,7 +22,7 @@ using osuTK; namespace osu.Game.Screens.Play { [Cached] - public class HUDOverlay : Container, IKeyBindingHandler, IDefaultSkinnableTarget + public class HUDOverlay : Container, IKeyBindingHandler { public const float FADE_DURATION = 300; diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 48da841c45..bfb5b8ef56 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -2,17 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.IO; -using System.Linq; -using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; -using osu.Game.Screens.Play.HUD; using osuTK.Graphics; namespace osu.Game.Skinning @@ -20,35 +14,11 @@ namespace osu.Game.Skinning public class DefaultSkin : Skin { public DefaultSkin() - : base(SkinInfo.Default) + : base(SkinInfo.Default, null) { Configuration = new DefaultSkinConfiguration(); } - public override Drawable GetDrawableComponent(ISkinComponent component) - { - switch (component) - { - case SkinnableTargetComponent target: - switch (target.Target) - { - case SkinnableTarget.MainHUDComponents: - var infos = JsonConvert.DeserializeObject>(File.ReadAllText("/Users/Dean/json-out.json")).Where(i => i.Target == SkinnableTarget.MainHUDComponents); - - var container = new SkinnableTargetWrapper(target.Target) - { - ChildrenEnumerable = infos.Select(i => i.CreateInstance()) - }; - - return container; - } - - break; - } - - return null; - } - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; public override ISample GetSample(ISampleInfo sampleInfo) => null; @@ -72,14 +42,4 @@ namespace osu.Game.Skinning return null; } } - - public class SkinnableTargetWrapper : Container, ISkinnableTarget - { - public SkinnableTarget Target { get; } - - public SkinnableTargetWrapper(SkinnableTarget target) - { - Target = target; - } - } } diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index a6f60afd90..45e541abf0 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.IO; using System.Linq; -using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Extensions; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components; @@ -32,11 +29,6 @@ namespace osu.Game.Skinning.Editor { ISkinnableComponent[] skinnableComponents = target.ChildrenOfType().ToArray(); - // todo: the OfType() call can be removed with better IDrawable support. - string json = JsonConvert.SerializeObject(skinnableComponents.OfType().Select(d => d.CreateSerialisedInformation()), new JsonSerializerSettings { Formatting = Formatting.Indented }); - - File.WriteAllText("/Users/Dean/json-out.json", json); - foreach (var c in skinnableComponents) AddBlueprintFor(c); diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 18a8b220df..30ee3097d2 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -11,6 +11,8 @@ using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning.Editor { @@ -24,6 +26,9 @@ namespace osu.Game.Skinning.Editor protected override bool StartHidden => true; + [Resolved] + private SkinManager skins { get; set; } + public SkinEditor(Drawable target) { this.target = target; @@ -53,7 +58,24 @@ namespace osu.Game.Skinning.Editor Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RequestPlacement = placeComponent - } + }, + new TriangleButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Padding = new MarginPadding + { + Top = 10, + Left = 10, + }, + Margin = new MarginPadding + { + Right = 10, + Bottom = 10, + }, + Action = save, + }, } }; @@ -71,7 +93,7 @@ namespace osu.Game.Skinning.Editor var targetContainer = target.ChildrenOfType().FirstOrDefault(); - targetContainer?.Add(instance); + (targetContainer as Container)?.Add(instance); } protected override void LoadComplete() @@ -80,6 +102,23 @@ namespace osu.Game.Skinning.Editor Show(); } + private void save() + { + var currentSkin = skins.CurrentSkin.Value; + + var legacySkin = currentSkin as LegacySkin; + + if (legacySkin == null) + return; + + SkinnableElementTargetContainer[] targetContainers = target.ChildrenOfType().ToArray(); + + foreach (var t in targetContainers) + legacySkin.UpdateDrawableTarget(t); + + skins.Save(skins.CurrentSkin.Value); + } + protected override bool OnHover(HoverEvent e) => true; protected override bool OnMouseDown(MouseDownEvent e) => true; diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 607e89fdec..90e48c8bba 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -2,14 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { /// /// Denotes a container which can house s. /// - public interface ISkinnableTarget : IContainerCollection + public interface ISkinnableTarget : IDrawable { + public SkinnableTarget Target { get; } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 70f8d1d882..fc03ce909c 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -58,7 +58,7 @@ namespace osu.Game.Skinning } protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string filename) - : base(skin) + : base(skin, resources) { using (var stream = storage?.GetStream(filename)) { @@ -321,8 +321,32 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(ISkinComponent component) { + if (base.GetDrawableComponent(component) is Drawable c) + return c; + switch (component) { + case SkinnableTargetComponent target: + + switch (target.Target) + { + case SkinnableTarget.MainHUDComponents: + return new SkinnableTargetWrapper(target.Target) + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + // TODO: these should fallback to the osu!classic skin. + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)) ?? new DefaultComboCounter(), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)) ?? new DefaultScoreCounter(), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)) ?? new DefaultAccuracyCounter(), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), + } + }; + } + + return null; + case HUDSkinComponent hudComponent: { if (!this.HasFont(LegacyFont.Score)) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 6d1bce2cb1..03ecabc886 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -2,12 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.IO; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning { @@ -15,9 +22,13 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; + private readonly IStorageResourceProvider resources; + public SkinConfiguration Configuration { get; protected set; } - public abstract Drawable GetDrawableComponent(ISkinComponent componentName); + public IDictionary DrawableComponentInfo => drawableComponentInfo; + + private readonly Dictionary drawableComponentInfo = new Dictionary(); public abstract ISample GetSample(ISampleInfo sampleInfo); @@ -27,9 +38,62 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); - protected Skin(SkinInfo skin) + protected Skin(SkinInfo skin, IStorageResourceProvider resources) { SkinInfo = skin; + + // may be null for default skin. + this.resources = resources; + } + + public void UpdateDrawableTarget(SkinnableElementTargetContainer targetContainer) + { + DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSerialisedChildren().ToArray(); + } + + public virtual Drawable GetDrawableComponent(ISkinComponent component) + { + switch (component) + { + case SkinnableTargetComponent target: + + var skinnableTarget = target.Target; + + if (!DrawableComponentInfo.TryGetValue(skinnableTarget, out var skinnableInfo)) + { + switch (skinnableTarget) + { + case SkinnableTarget.MainHUDComponents: + + // skininfo files may be null for default skin. + var fileInfo = SkinInfo.Files?.FirstOrDefault(f => f.Filename == $"{skinnableTarget}.json"); + + if (fileInfo == null) + return null; + + var bytes = resources?.Files.Get(fileInfo.FileInfo.StoragePath); + + if (bytes == null) + return null; + + string jsonContent = Encoding.UTF8.GetString(bytes); + + DrawableComponentInfo[skinnableTarget] = skinnableInfo = + JsonConvert.DeserializeObject>(jsonContent).Where(i => i.Target == SkinnableTarget.MainHUDComponents).ToArray(); + break; + } + } + + if (skinnableInfo == null) + return null; + + return new SkinnableTargetWrapper(skinnableTarget) + { + ChildrenEnumerable = skinnableInfo.Select(i => i.CreateInstance()) + }; + } + + return null; } #region Disposal @@ -58,4 +122,17 @@ namespace osu.Game.Skinning #endregion } + + public class SkinnableTargetWrapper : Container, IDefaultSkinnableTarget + { + // this is just here to implement the interface and allow a silly parent lookup to work (where the target is not the direct parent for reasons). + // TODO: need to fix this. + public SkinnableTarget Target { get; } + + public SkinnableTargetWrapper(SkinnableTarget target) + { + Target = target; + RelativeSizeAxes = Axes.Both; + } + } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ac4d63159a..f9e22c9c89 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -6,9 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -17,6 +19,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; @@ -158,6 +161,35 @@ namespace osu.Game.Skinning return new LegacySkin(skinInfo, this); } + public void Save(Skin skin) + { + // some skins don't support saving just yet. + // eventually we will want to create a copy of the skin to allow for customisation. + if (skin.SkinInfo.Files == null) + return; + + foreach (var drawableInfo in skin.DrawableComponentInfo) + { + // todo: the OfType() call can be removed with better IDrawable support. + string json = JsonConvert.SerializeObject(drawableInfo.Value, new JsonSerializerSettings { Formatting = Formatting.Indented }); + + using (var streamContent = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + string filename = $"{drawableInfo.Key}.json"; + + var oldFile = skin.SkinInfo.Files.FirstOrDefault(f => f.Filename == filename); + + if (oldFile != null) + ReplaceFile(skin.SkinInfo, oldFile, streamContent, oldFile.Filename); + else + AddFile(skin.SkinInfo, streamContent, filename); + + Logger.Log($"Saving out {filename} with {json.Length} bytes"); + Logger.Log(json); + } + } + } + /// /// Perform a lookup query on available s. /// From 1bb3c609aeec1c773aac2a7a28f4c21d52674052 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 11:00:57 +0900 Subject: [PATCH 044/215] Centralise implementation of cancel button logic --- .../UserInterface/DangerousTriangleButton.cs | 18 ++++++++++++++++++ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 11 +---------- .../KeyBinding/KeyBindingsSubsection.cs | 9 ++------- 3 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/DangerousTriangleButton.cs diff --git a/osu.Game/Graphics/UserInterface/DangerousTriangleButton.cs b/osu.Game/Graphics/UserInterface/DangerousTriangleButton.cs new file mode 100644 index 0000000000..89a4c28c8c --- /dev/null +++ b/osu.Game/Graphics/UserInterface/DangerousTriangleButton.cs @@ -0,0 +1,18 @@ +// 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; + +namespace osu.Game.Graphics.UserInterface +{ + public class DangerousTriangleButton : TriangleButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.PinkDark; + Triangles.ColourDark = colours.PinkDarker; + Triangles.ColourLight = colours.Pink; + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 300fce962a..43942d2d52 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -339,22 +339,13 @@ namespace osu.Game.Overlays.KeyBinding } } - public class ClearButton : TriangleButton + public class ClearButton : DangerousTriangleButton { public ClearButton() { Text = "Clear"; Size = new Vector2(80, 20); } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Pink; - - Triangles.ColourDark = colours.PinkDark; - Triangles.ColourLight = colours.PinkLight; - } } public class KeyButton : Container diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index d784b7aec9..707176e63e 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -11,7 +11,6 @@ using osu.Game.Input; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osuTK; -using osu.Game.Graphics; namespace osu.Game.Overlays.KeyBinding { @@ -55,10 +54,10 @@ namespace osu.Game.Overlays.KeyBinding } } - public class ResetButton : TriangleButton + public class ResetButton : DangerousTriangleButton { [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { Text = "Reset all bindings in section"; RelativeSizeAxes = Axes.X; @@ -66,10 +65,6 @@ namespace osu.Game.Overlays.KeyBinding Height = 20; Content.CornerRadius = 5; - - BackgroundColour = colours.PinkDark; - Triangles.ColourDark = colours.PinkDarker; - Triangles.ColourLight = colours.Pink; } } } From c957293ec3a580875f7961bcde4e47bfd20c2a27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 11:54:45 +0900 Subject: [PATCH 045/215] Load json from disk store at skin construction for now This allows for easier mutation without worrying about changes being re-read from disk unexpectedly. --- osu.Game/Skinning/Skin.cs | 62 ++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 03ecabc886..34571ec688 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -44,6 +44,32 @@ namespace osu.Game.Skinning // may be null for default skin. this.resources = resources; + + // we may want to move this to some kind of async operation in the future. + foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget))) + { + string filename = $"{skinnableTarget}.json"; + + // skininfo files may be null for default skin. + var fileInfo = SkinInfo.Files?.FirstOrDefault(f => f.Filename == filename); + + if (fileInfo == null) + continue; + + var bytes = resources?.Files.Get(fileInfo.FileInfo.StoragePath); + + if (bytes == null) + continue; + + string jsonContent = Encoding.UTF8.GetString(bytes); + + DrawableComponentInfo[skinnableTarget] = JsonConvert.DeserializeObject>(jsonContent).ToArray(); + } + } + + public void ResetDrawableTarget(SkinnableElementTargetContainer targetContainer) + { + DrawableComponentInfo.Remove(targetContainer.Target); } public void UpdateDrawableTarget(SkinnableElementTargetContainer targetContainer) @@ -60,34 +86,9 @@ namespace osu.Game.Skinning var skinnableTarget = target.Target; if (!DrawableComponentInfo.TryGetValue(skinnableTarget, out var skinnableInfo)) - { - switch (skinnableTarget) - { - case SkinnableTarget.MainHUDComponents: - - // skininfo files may be null for default skin. - var fileInfo = SkinInfo.Files?.FirstOrDefault(f => f.Filename == $"{skinnableTarget}.json"); - - if (fileInfo == null) - return null; - - var bytes = resources?.Files.Get(fileInfo.FileInfo.StoragePath); - - if (bytes == null) - return null; - - string jsonContent = Encoding.UTF8.GetString(bytes); - - DrawableComponentInfo[skinnableTarget] = skinnableInfo = - JsonConvert.DeserializeObject>(jsonContent).Where(i => i.Target == SkinnableTarget.MainHUDComponents).ToArray(); - break; - } - } - - if (skinnableInfo == null) return null; - return new SkinnableTargetWrapper(skinnableTarget) + return new SkinnableTargetWrapper { ChildrenEnumerable = skinnableInfo.Select(i => i.CreateInstance()) }; @@ -123,15 +124,10 @@ namespace osu.Game.Skinning #endregion } - public class SkinnableTargetWrapper : Container, IDefaultSkinnableTarget + public class SkinnableTargetWrapper : Container, ISkinnableComponent { - // this is just here to implement the interface and allow a silly parent lookup to work (where the target is not the direct parent for reasons). - // TODO: need to fix this. - public SkinnableTarget Target { get; } - - public SkinnableTargetWrapper(SkinnableTarget target) + public SkinnableTargetWrapper() { - Target = target; RelativeSizeAxes = Axes.Both; } } From 4769a95b4994b032f64e446272ffed0051230f43 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 11:56:14 +0900 Subject: [PATCH 046/215] Fix encapsulation and remove target lookup overhead --- .../HUD/SkinnableElementTargetContainer.cs | 23 ++++++++++++++----- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 9 -------- osu.Game/Skinning/IDefaultSkinnableTarget.cs | 12 ---------- osu.Game/Skinning/ISkinnableTarget.cs | 12 +++++++++- osu.Game/Skinning/LegacySkin.cs | 3 +-- 5 files changed, 29 insertions(+), 30 deletions(-) delete mode 100644 osu.Game/Skinning/IDefaultSkinnableTarget.cs diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index f1b282119b..79a0db1751 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; using osu.Game.Extensions; using osu.Game.Skinning; @@ -19,6 +20,21 @@ namespace osu.Game.Screens.Play.HUD Target = target; } + public void Reload() + { + content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; + + ClearInternal(); + + if (content != null) + LoadComponentAsync(content, AddInternal); + } + + public void Add(Drawable drawable) + { + content.Add(drawable); + } + public IEnumerable CreateSerialisedChildren() => content.Select(d => d.CreateSerialisedInformation()); @@ -26,12 +42,7 @@ namespace osu.Game.Screens.Play.HUD { base.SkinChanged(skin, allowFallback); - content = skin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; - - ClearInternal(); - - if (content != null) - LoadComponentAsync(content, AddInternal); + Reload(); } } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index ec4eaa0e59..abeb96f647 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Extensions; @@ -23,9 +21,6 @@ namespace osu.Game.Screens.Play.HUD { public Type Type { get; set; } - [JsonConverter(typeof(StringEnumConverter))] - public SkinnableTarget? Target { get; set; } - public Vector2 Position { get; set; } public float Rotation { get; set; } @@ -44,10 +39,6 @@ namespace osu.Game.Screens.Play.HUD { Type = component.GetType(); - ISkinnableTarget target = component.Parent as ISkinnableTarget; - - Target = target?.Target; - Position = component.Position; Rotation = component.Rotation; Scale = component.Scale; diff --git a/osu.Game/Skinning/IDefaultSkinnableTarget.cs b/osu.Game/Skinning/IDefaultSkinnableTarget.cs deleted file mode 100644 index 24fb454af8..0000000000 --- a/osu.Game/Skinning/IDefaultSkinnableTarget.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Skinning -{ - /// - /// The default placement location for new s. - /// - public interface IDefaultSkinnableTarget : ISkinnableTarget - { - } -} diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 90e48c8bba..2379fd4247 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -8,8 +8,18 @@ namespace osu.Game.Skinning /// /// Denotes a container which can house s. /// - public interface ISkinnableTarget : IDrawable + public interface ISkinnableTarget { public SkinnableTarget Target { get; } + + /// + /// Reload this target from the current skin. + /// + public void Reload(); + + /// + /// Add the provided item to this target. + /// + public void Add(Drawable drawable); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index fc03ce909c..3ffd2245c2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -331,9 +331,8 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: - return new SkinnableTargetWrapper(target.Target) + return new SkinnableTargetWrapper { - RelativeSizeAxes = Axes.Both, Children = new[] { // TODO: these should fallback to the osu!classic skin. From 81902ad6a688f2e143d06234006027889d8808be Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 11:57:12 +0900 Subject: [PATCH 047/215] Add the ability to revert all skin changes --- osu.Game/Skinning/Editor/SkinEditor.cs | 66 +++++++++++++++++++++----- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 30ee3097d2..6b7d289284 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; +using osuTK; namespace osu.Game.Skinning.Editor { @@ -20,7 +21,7 @@ namespace osu.Game.Skinning.Editor { public const double TRANSITION_DURATION = 500; - private readonly Drawable target; + private readonly Drawable targetScreen; private OsuTextFlowContainer headerText; @@ -29,9 +30,9 @@ namespace osu.Game.Skinning.Editor [Resolved] private SkinManager skins { get; set; } - public SkinEditor(Drawable target) + public SkinEditor(Drawable targetScreen) { - this.target = target; + this.targetScreen = targetScreen; RelativeSizeAxes = Axes.Both; } @@ -52,18 +53,20 @@ namespace osu.Game.Skinning.Editor Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X }, - new SkinBlueprintContainer(target), + new SkinBlueprintContainer(targetScreen), new SkinComponentToolbox(600) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RequestPlacement = placeComponent }, - new TriangleButton + new FillFlowContainer { - Text = "Save Changes", - Width = 140, - Height = 50, + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Spacing = new Vector2(5), Padding = new MarginPadding { Top = 10, @@ -74,7 +77,21 @@ namespace osu.Game.Skinning.Editor Right = 10, Bottom = 10, }, - Action = save, + Children = new Drawable[] + { + new TriangleButton + { + Text = "Save Changes", + Width = 140, + Action = save, + }, + new DangerousTriangleButton + { + Text = "Revert to default", + Width = 140, + Action = revert, + }, + } }, } }; @@ -89,11 +106,14 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { - var instance = (Drawable)Activator.CreateInstance(type); + Drawable instance = (Drawable)Activator.CreateInstance(type); - var targetContainer = target.ChildrenOfType().FirstOrDefault(); + getTarget(SkinnableTarget.MainHUDComponents)?.Add(instance); + } - (targetContainer as Container)?.Add(instance); + private ISkinnableTarget getTarget(SkinnableTarget target) + { + return targetScreen.ChildrenOfType().FirstOrDefault(c => c.Target == target); } protected override void LoadComplete() @@ -102,6 +122,26 @@ namespace osu.Game.Skinning.Editor Show(); } + private void revert() + { + var currentSkin = skins.CurrentSkin.Value; + + var legacySkin = currentSkin as LegacySkin; + + if (legacySkin == null) + return; + + SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); + + foreach (var t in targetContainers) + { + legacySkin.ResetDrawableTarget(t); + + // add back default components + getTarget(t.Target).Reload(); + } + } + private void save() { var currentSkin = skins.CurrentSkin.Value; @@ -111,7 +151,7 @@ namespace osu.Game.Skinning.Editor if (legacySkin == null) return; - SkinnableElementTargetContainer[] targetContainers = target.ChildrenOfType().ToArray(); + SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) legacySkin.UpdateDrawableTarget(t); From bf65547eec7419273bd106f30d4842dd19ea86fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 12:04:25 +0900 Subject: [PATCH 048/215] Allow some serialised components to not be mutable by the user --- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 4 ++-- osu.Game/Skinning/IImmutableSkinnableComponent.cs | 15 +++++++++++++++ osu.Game/Skinning/ISkinnableComponent.cs | 4 +--- osu.Game/Skinning/Skin.cs | 5 +---- 4 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Skinning/IImmutableSkinnableComponent.cs diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index abeb96f647..988d73fed6 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { /// - /// Serialised information governing custom changes to an . + /// Serialised information governing custom changes to an . /// [Serializable] public class SkinnableInfo : IJsonSerializable @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play.HUD if (component is Container container) { - foreach (var child in container.Children.OfType().OfType()) + foreach (var child in container.Children.OfType().OfType()) Children.Add(child.CreateSerialisedInformation()); } } diff --git a/osu.Game/Skinning/IImmutableSkinnableComponent.cs b/osu.Game/Skinning/IImmutableSkinnableComponent.cs new file mode 100644 index 0000000000..d1777512af --- /dev/null +++ b/osu.Game/Skinning/IImmutableSkinnableComponent.cs @@ -0,0 +1,15 @@ +// 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.Graphics; + +namespace osu.Game.Skinning +{ + /// + /// Denotes a drawable component which should be serialised as part of a skin. + /// Use for components which should be mutable by the user / editor. + /// + public interface ISkinSerialisable : IDrawable + { + } +} diff --git a/osu.Game/Skinning/ISkinnableComponent.cs b/osu.Game/Skinning/ISkinnableComponent.cs index f6b0a182b4..e44c7be13d 100644 --- a/osu.Game/Skinning/ISkinnableComponent.cs +++ b/osu.Game/Skinning/ISkinnableComponent.cs @@ -1,14 +1,12 @@ // 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.Graphics; - namespace osu.Game.Skinning { /// /// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications. /// - public interface ISkinnableComponent : IDrawable + public interface ISkinnableComponent : ISkinSerialisable { } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 34571ec688..cbbad4a607 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -22,8 +22,6 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo; - private readonly IStorageResourceProvider resources; - public SkinConfiguration Configuration { get; protected set; } public IDictionary DrawableComponentInfo => drawableComponentInfo; @@ -43,7 +41,6 @@ namespace osu.Game.Skinning SkinInfo = skin; // may be null for default skin. - this.resources = resources; // we may want to move this to some kind of async operation in the future. foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget))) @@ -124,7 +121,7 @@ namespace osu.Game.Skinning #endregion } - public class SkinnableTargetWrapper : Container, ISkinnableComponent + public class SkinnableTargetWrapper : Container, ISkinSerialisable { public SkinnableTargetWrapper() { From 2396ba42a6ec6c6e9f3e4b8d89513fec123706e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 12:21:31 +0900 Subject: [PATCH 049/215] Change `HealthDisplay` to be a `CompositeDrawable` --- osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs | 6 ++++-- osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/FailingLayer.cs | 2 +- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index fb4c9d713a..c335f7c99e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -1,11 +1,13 @@ // 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.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; @@ -50,9 +52,9 @@ namespace osu.Game.Tests.Visual.Gameplay }); AddStep("set health to 0.10", () => layer.Current.Value = 0.1); - AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f); + AddUntilStep("layer fade is visible", () => layer.ChildrenOfType().First().Alpha > 0.1f); AddStep("set health to 1", () => layer.Current.Value = 1f); - AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent); + AddUntilStep("layer fade is invisible", () => !layer.ChildrenOfType().First().IsPresent); } [Test] diff --git a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs index 37bd289aee..241777244b 100644 --- a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.X; Margin = new MarginPadding { Top = 20 }; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index e071337f34..424ee55766 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play.HUD public FailingLayer() { RelativeSizeAxes = Axes.Both; - Children = new Drawable[] + InternalChildren = new Drawable[] { boxes = new Container { diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index e18a28eded..5a2c764a1a 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Play.HUD /// A container for components displaying the current player health. /// Gets bound automatically to the when inserted to hierarchy. /// - public abstract class HealthDisplay : Container + public abstract class HealthDisplay : CompositeDrawable { private Bindable showHealthbar; From 4c4d75e6f9c341548b310a767cc15d6839559905 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 12:42:32 +0900 Subject: [PATCH 050/215] Remove `AccuracyCounter` sizing dependency in `HUDOverlay` --- .../HUD/SkinnableElementTargetContainer.cs | 2 ++ osu.Game/Screens/Play/HUDOverlay.cs | 34 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index 79a0db1751..051ab9ad3c 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -20,6 +20,8 @@ namespace osu.Game.Screens.Play.HUD Target = target; } + public IReadOnlyList Children => content?.Children; + public void Reload() { content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 30b735d28a..033a280da2 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -35,7 +36,6 @@ namespace osu.Game.Screens.Play public readonly KeyCounterDisplay KeyCounter; public readonly SkinnableScoreCounter ScoreCounter; - public readonly SkinnableAccuracyCounter AccuracyCounter; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; @@ -68,6 +68,8 @@ namespace osu.Game.Screens.Play private bool holdingForHUD; + private readonly SkinnableElementTargetContainer mainComponents; + private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList mods) @@ -95,7 +97,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new SkinnableElementTargetContainer(SkinnableTarget.MainHUDComponents) + mainComponents = new SkinnableElementTargetContainer(SkinnableTarget.MainHUDComponents) { RelativeSizeAxes = Axes.Both, }, @@ -107,7 +109,6 @@ namespace osu.Game.Screens.Play { // remaining cross-dependencies need tidying. // kept to ensure non-null, but hidden for testing. - AccuracyCounter = new SkinnableAccuracyCounter(), ScoreCounter = new SkinnableScoreCounter(), } }, @@ -216,12 +217,29 @@ namespace osu.Game.Screens.Play { base.Update(); - // HACK: for now align with the accuracy counter. - // this is done for the sake of hacky legacy skins which extend the health bar to take up the full screen area. - // it only works with the default skin due to padding offsetting it *just enough* to coexist. - topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(AccuracyCounter.Drawable.ScreenSpaceDrawQuad.BottomRight).Y; + Vector2 lowestScreenSpace = Vector2.Zero; - bottomRightElements.Y = -Progress.Height; + // TODO: may be null during skin switching. not sure if there's a better way of exposing these children. + if (mainComponents.Children != null) + { + foreach (var element in mainComponents.Children) + { + // for now align top-right components with the bottom-edge of the lowest top-anchored hud element. + if (!element.Anchor.HasFlagFast(Anchor.TopRight)) + continue; + + // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. + if (element is HealthDisplay) + continue; + + var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; + if (bottomRight.Y > lowestScreenSpace.Y) + lowestScreenSpace = bottomRight; + } + + topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; + bottomRightElements.Y = -Progress.Height; + } } private void updateVisibility() From 117d6d731d1a0f5de4648888cfd91f180b1cac2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 13:12:24 +0900 Subject: [PATCH 051/215] Move cross-component layout dependencies for legacy skin to `LegacySkin` --- osu.Game/Skinning/LegacyAccuracyCounter.cs | 11 ------ osu.Game/Skinning/LegacySkin.cs | 15 +++++++- osu.Game/Skinning/Skin.cs | 9 ----- osu.Game/Skinning/SkinnableTargetWrapper.cs | 41 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 osu.Game/Skinning/SkinnableTargetWrapper.cs diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index e2fe01efe4..3eea5d22d5 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -32,16 +32,5 @@ namespace osu.Game.Skinning Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }; - - protected override void Update() - { - base.Update(); - - if (hud?.ScoreCounter?.Drawable is LegacyScoreCounter score) - { - // for now align with the score counter. eventually this will be user customisable. - Y = Parent.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; - } - } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3ffd2245c2..85cfab8297 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -327,11 +327,20 @@ namespace osu.Game.Skinning switch (component) { case SkinnableTargetComponent target: - switch (target.Target) { case SkinnableTarget.MainHUDComponents: - return new SkinnableTargetWrapper + + var skinnableTargetWrapper = new SkinnableTargetWrapper(container => + { + var score = container.OfType().FirstOrDefault(); + var accuracy = container.OfType().FirstOrDefault(); + + if (score != null && accuracy != null) + { + accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y; + } + }) { Children = new[] { @@ -342,6 +351,8 @@ namespace osu.Game.Skinning GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)) ?? new DefaultHealthDisplay(), } }; + + return skinnableTargetWrapper; } return null; diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index cbbad4a607..10481e4efd 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -9,7 +9,6 @@ using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; @@ -120,12 +119,4 @@ namespace osu.Game.Skinning #endregion } - - public class SkinnableTargetWrapper : Container, ISkinSerialisable - { - public SkinnableTargetWrapper() - { - RelativeSizeAxes = Axes.Both; - } - } } diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs new file mode 100644 index 0000000000..a0df610488 --- /dev/null +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -0,0 +1,41 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Skinning +{ + /// + /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type. + /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. + /// + public class SkinnableTargetWrapper : Container, ISkinSerialisable + { + private readonly Action applyDefaults; + + /// + /// Construct a wrapper with defaults that should be applied once. + /// + /// A function with default to apply after the initial layout (ie. consuming autosize) + public SkinnableTargetWrapper(Action applyDefaults) + : this() + { + this.applyDefaults = applyDefaults; + } + + public SkinnableTargetWrapper() + { + RelativeSizeAxes = Axes.Both; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // schedule is required to allow children to run their LoadComplete and take on their correct sizes. + Schedule(() => applyDefaults?.Invoke(this)); + } + } +} From f53ce951dc1ee6594d5a6cc4e975fa9e9a15dee6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 13:13:40 +0900 Subject: [PATCH 052/215] Remove `DefaultScoreCounter` animation for the time being May add this back in the future, but for now it's causing issues as it operates on `this`. The default skin may be changing quite a bit in the near future, so we can decide what to do about animation at that point in time. --- osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs index 8e37797446..bd18050dfb 100644 --- a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs @@ -24,12 +24,6 @@ namespace osu.Game.Screens.Play.HUD private void load(OsuColour colours) { Colour = colours.BlueLighter; - - // todo: check if default once health display is skinnable - hud?.ShowHealthbar.BindValueChanged(healthBar => - { - this.MoveToY(healthBar.NewValue ? 30 : 0, HUDOverlay.FADE_DURATION, HUDOverlay.FADE_EASING); - }, true); } } } From c94df672e5f30c4d87738ca37a019685773c4df2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 13:39:32 +0900 Subject: [PATCH 053/215] Also serialise `Origin` out --- osu.Game/Extensions/DrawableExtensions.cs | 1 + osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 6837a802f2..0ec96a876e 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -55,6 +55,7 @@ namespace osu.Game.Extensions component.Rotation = info.Rotation; component.Scale = info.Scale; component.Anchor = info.Anchor; + component.Origin = info.Origin; if (component is Container container) { diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 988d73fed6..2de3f1b729 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -29,6 +29,8 @@ namespace osu.Game.Screens.Play.HUD public Anchor Anchor { get; set; } + public Anchor Origin { get; set; } + public List Children { get; } = new List(); public SkinnableInfo() @@ -43,6 +45,7 @@ namespace osu.Game.Screens.Play.HUD Rotation = component.Rotation; Scale = component.Scale; Anchor = component.Anchor; + Origin = component.Origin; if (component is Container container) { From 12684de66eb76365a3f10d4bee629eb9f0ada1d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 14:09:56 +0900 Subject: [PATCH 054/215] Add ability to adjust origin in skin editor --- .../Skinning/Editor/SkinSelectionHandler.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index ad783a9c0e..cf5ece03e9 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -70,13 +70,18 @@ namespace osu.Game.Skinning.Editor { yield return new OsuMenuItem("Anchor") { - Items = createAnchorItems().ToArray() + Items = createAnchorItems(d => d.Anchor, applyAnchor).ToArray() + }; + + yield return new OsuMenuItem("Origin") + { + Items = createAnchorItems(d => d.Origin, applyOrigin).ToArray() }; foreach (var item in base.GetContextMenuItemsForSelection(selection)) yield return item; - IEnumerable createAnchorItems() + IEnumerable createAnchorItems(Func checkFunction, Action applyFunction) { var displayableAnchors = new[] { @@ -93,14 +98,20 @@ namespace osu.Game.Skinning.Editor return displayableAnchors.Select(a => { - return new AnchorMenuItem(a, selection, _ => applyAnchor(a)) + return new AnchorMenuItem(a, selection, _ => applyFunction(a)) { - State = { Value = GetStateFromSelection(selection, c => ((Drawable)c.Item).Anchor == a) } + State = { Value = GetStateFromSelection(selection, c => checkFunction((Drawable)c.Item) == a) } }; }); } } + private void applyOrigin(Anchor anchor) + { + foreach (var item in SelectedItems) + ((Drawable)item).Origin = anchor; + } + private void applyAnchor(Anchor anchor) { foreach (var item in SelectedItems) From 944f09ec98e54f296ee6fe04a0b183ab31bd4550 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 14:12:28 +0900 Subject: [PATCH 055/215] Move default skin cross-component dependencies out to default specifications --- .../Play/HUD/DefaultAccuracyCounter.cs | 22 ------- .../Screens/Play/HUD/DefaultComboCounter.cs | 14 ---- osu.Game/Screens/Play/HUDOverlay.cs | 12 ---- osu.Game/Skinning/DefaultSkin.cs | 64 +++++++++++++++++++ 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs index e4c865803d..d8dff89b29 100644 --- a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs @@ -2,23 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Screens.Play.HUD { public class DefaultAccuracyCounter : GameplayAccuracyCounter, ISkinnableComponent { - private readonly Vector2 offset = new Vector2(-20, 5); - - public DefaultAccuracyCounter() - { - Origin = Anchor.TopRight; - Anchor = Anchor.TopRight; - } - [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } @@ -27,17 +17,5 @@ namespace osu.Game.Screens.Play.HUD { Colour = colours.BlueLighter; } - - protected override void Update() - { - base.Update(); - - if (hud?.ScoreCounter.Drawable is DefaultScoreCounter score) - { - // for now align with the score counter. eventually this will be user customisable. - Anchor = Anchor.TopLeft; - Position = Parent.ToLocalSpace(score.ScreenSpaceDrawQuad.TopLeft) + offset; - } - } } } diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs index 375ff293aa..13bd045fc0 100644 --- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs @@ -9,14 +9,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; -using osuTK; namespace osu.Game.Screens.Play.HUD { public class DefaultComboCounter : RollingCounter, ISkinnableComponent { - private readonly Vector2 offset = new Vector2(20, 5); - [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } @@ -32,17 +29,6 @@ namespace osu.Game.Screens.Play.HUD Current.BindTo(scoreProcessor.Combo); } - protected override void Update() - { - base.Update(); - - if (hud?.ScoreCounter.Drawable is DefaultScoreCounter score) - { - // for now align with the score counter. eventually this will be user customisable. - Position = Parent.ToLocalSpace(score.ScreenSpaceDrawQuad.TopRight) + offset; - } - } - protected override string FormatCount(int count) { return $@"{count}x"; diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 033a280da2..88176e7ea2 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -35,7 +35,6 @@ namespace osu.Game.Screens.Play public float TopScoringElementsHeight { get; private set; } public readonly KeyCounterDisplay KeyCounter; - public readonly SkinnableScoreCounter ScoreCounter; public readonly SongProgress Progress; public readonly ModDisplay ModDisplay; public readonly HoldForMenuButton HoldToQuit; @@ -102,17 +101,6 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, }, new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Children = new Drawable[] - { - // remaining cross-dependencies need tidying. - // kept to ensure non-null, but hidden for testing. - ScoreCounter = new SkinnableScoreCounter(), - } - }, - new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index bfb5b8ef56..ef3dffe59c 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -2,11 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.Extensions; +using osu.Game.Screens.Play.HUD; +using osuTK; using osuTK.Graphics; namespace osu.Game.Skinning @@ -23,6 +28,65 @@ namespace osu.Game.Skinning public override ISample GetSample(ISampleInfo sampleInfo) => null; + public override Drawable GetDrawableComponent(ISkinComponent component) + { + switch (component) + { + case SkinnableTargetComponent target: + switch (target.Target) + { + case SkinnableTarget.MainHUDComponents: + var skinnableTargetWrapper = new SkinnableTargetWrapper(container => + { + var score = container.OfType().FirstOrDefault(); + var accuracy = container.OfType().FirstOrDefault(); + var combo = container.OfType().FirstOrDefault(); + + if (score != null) + { + score.Anchor = Anchor.TopCentre; + score.Origin = Anchor.TopCentre; + + // elements default to beneath the health bar + const float vertical_offset = 30; + + const float horizontal_padding = 20; + + score.Position = new Vector2(0, vertical_offset); + + if (accuracy != null) + { + accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5); + accuracy.Origin = Anchor.TopRight; + accuracy.Anchor = Anchor.TopCentre; + } + + if (combo != null) + { + combo.Position = new Vector2(accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 + horizontal_padding, vertical_offset + 5); + combo.Anchor = Anchor.TopCentre; + } + } + }) + { + Children = new Drawable[] + { + new DefaultComboCounter(), + new DefaultScoreCounter(), + new DefaultAccuracyCounter(), + new DefaultHealthDisplay(), + } + }; + + return skinnableTargetWrapper; + } + + return null; + } + + return base.GetDrawableComponent(component); + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) From 03d5f10744b91aa189d5bf1fe5a153a2117830b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 14:22:19 +0900 Subject: [PATCH 056/215] Fix default health bar not being considered for top-right flow layout --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 88176e7ea2..c4a8860bb6 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -213,11 +213,11 @@ namespace osu.Game.Screens.Play foreach (var element in mainComponents.Children) { // for now align top-right components with the bottom-edge of the lowest top-anchored hud element. - if (!element.Anchor.HasFlagFast(Anchor.TopRight)) + if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X)) continue; // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. - if (element is HealthDisplay) + if (element is LegacyHealthDisplay) continue; var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; From d5fe4f0f72e404f9527e2ccfe444dcd6645de0fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 14:34:49 +0900 Subject: [PATCH 057/215] Remove unused skin resolution in `LegacyScoreCounter` --- osu.Game/Skinning/LegacyScoreCounter.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index 0696d2bedd..8aa48da453 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Screens.Play.HUD; @@ -14,9 +13,6 @@ namespace osu.Game.Skinning protected override double RollingDuration => 1000; protected override Easing RollingEasing => Easing.Out; - [Resolved] - private ISkinSource skin { get; set; } - public LegacyScoreCounter() : base(6) { From 0cf3efa16b9c20dccd26722e599ffa75bc5b9b72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 16:56:23 +0900 Subject: [PATCH 058/215] Remove customisation support for `SongProgressDisplay` --- osu.Game/Screens/Play/SongProgress.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index d85f3538e4..4a0787bfae 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -14,7 +14,6 @@ using osu.Framework.Timing; using osu.Game.Configuration; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; namespace osu.Game.Screens.Play { @@ -181,12 +180,12 @@ namespace osu.Game.Screens.Play info.TransformTo(nameof(info.Margin), new MarginPadding { Bottom = finalMargin }, transition_duration, Easing.In); } - public class SongProgressDisplay : Container, ISkinnableComponent + public class SongProgressDisplay : Container { public SongProgressDisplay() { // TODO: move actual implementation into this. - // exists for skin customisation purposes. + // exists for skin customisation purposes (interface should be added to this container). Masking = true; RelativeSizeAxes = Axes.Both; From f6f4b90d2bd71680a470d92c0aef5a98494e8580 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 16:56:55 +0900 Subject: [PATCH 059/215] Add customisation support for `LegacyHealthDisplay` --- osu.Game/Skinning/LegacyHealthDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 717ca62da7..dfb6ca1a64 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -16,7 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacyHealthDisplay : HealthDisplay + public class LegacyHealthDisplay : HealthDisplay, ISkinnableComponent { private const double epic_cutoff = 0.5; From a67cead0b3adfce14608060d51131145ff653c81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:00:24 +0900 Subject: [PATCH 060/215] Add `SkinInfo.InstantiationInfo` to allow creating different skin types --- .../TestSceneHitCircleArea.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- ...60743_AddSkinInstantiationInfo.Designer.cs | 508 ++++++++++++++++++ ...20210511060743_AddSkinInstantiationInfo.cs | 22 + .../Migrations/OsuDbContextModelSnapshot.cs | 6 +- osu.Game/Skinning/DefaultLegacySkin.cs | 10 +- osu.Game/Skinning/DefaultSkin.cs | 10 +- osu.Game/Skinning/SkinInfo.cs | 36 +- osu.Game/Skinning/SkinManager.cs | 22 +- 9 files changed, 597 insertions(+), 21 deletions(-) create mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs create mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs index 0649989dc0..1fdcd73dde 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = new SkinProvidingContainer(new DefaultSkin()) + Child = new SkinProvidingContainer(new DefaultSkin(null)) { RelativeSizeAxes = Axes.Both, Child = drawableHitCircle = new DrawableHitCircle(hitCircle) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index e0eeaf6db0..3576b149bf 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected virtual ISkin GetSkin() => new DefaultSkin(); + protected virtual ISkin GetSkin() => new DefaultSkin(null); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs new file mode 100644 index 0000000000..b808c648da --- /dev/null +++ b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs @@ -0,0 +1,508 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210511060743_AddSkinInstantiationInfo")] + partial class AddSkinInstantiationInfo + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("EpilepsyWarning"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs new file mode 100644 index 0000000000..1d5b0769a4 --- /dev/null +++ b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddSkinInstantiationInfo : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "InstantiationInfo", + table: "SkinInfo", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "InstantiationInfo", + table: "SkinInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index ec4461ca56..d4bde50b60 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -141,8 +141,6 @@ namespace osu.Game.Migrations b.Property("TitleUnicode"); - b.Property("VideoFile"); - b.HasKey("ID"); b.ToTable("BeatmapMetadata"); @@ -352,7 +350,7 @@ namespace osu.Game.Migrations b.Property("TotalScore"); - b.Property("UserID") + b.Property("UserID") .HasColumnName("UserID"); b.Property("UserString") @@ -402,6 +400,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("InstantiationInfo"); + b.Property("Name"); b.HasKey("ID"); diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 4027cc650d..564be8630e 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -10,7 +10,12 @@ namespace osu.Game.Skinning public class DefaultLegacySkin : LegacySkin { public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) - : base(Info, storage, resources, string.Empty) + : this(Info, storage, resources) + { + } + + public DefaultLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources) + : base(skin, storage, resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.AddComboColours( @@ -27,7 +32,8 @@ namespace osu.Game.Skinning { ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. Name = "osu!classic", - Creator = "team osu!" + Creator = "team osu!", + InstantiationInfo = typeof(DefaultLegacySkin).AssemblyQualifiedName, }; } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index ef3dffe59c..1c063b6ef2 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Extensions; +using osu.Game.IO; using osu.Game.Screens.Play.HUD; using osuTK; using osuTK.Graphics; @@ -18,8 +19,13 @@ namespace osu.Game.Skinning { public class DefaultSkin : Skin { - public DefaultSkin() - : base(SkinInfo.Default, null) + public DefaultSkin(IStorageResourceProvider resources) + : this(SkinInfo.Default, resources) + { + } + + public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources) + : base(skin, resources) { Configuration = new DefaultSkinConfiguration(); } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index aaccbefb3d..2e29808cb4 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.IO; namespace osu.Game.Skinning { @@ -22,7 +25,35 @@ namespace osu.Game.Skinning public string Creator { get; set; } - public List Files { get; set; } + private string instantiationInfo; + + public string InstantiationInfo + { + get => instantiationInfo; + set => instantiationInfo = abbreviateInstantiationInfo(value); + } + + private string abbreviateInstantiationInfo(string value) + { + // exclude version onwards, matching only on namespace and type. + // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. + return string.Join(',', value.Split(',').Take(2)); + } + + public virtual Skin CreateInstance(IResourceStore legacyDefaultResources, IStorageResourceProvider resources) + { + var type = string.IsNullOrEmpty(InstantiationInfo) + // handle the case of skins imported before InstantiationInfo was added. + ? typeof(LegacySkin) + : Type.GetType(InstantiationInfo); + + if (type == typeof(DefaultLegacySkin)) + return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources); + + return (Skin)Activator.CreateInstance(type, this, resources); + } + + public List Files { get; set; } = new List(); public List Settings { get; set; } @@ -32,7 +63,8 @@ namespace osu.Game.Skinning { ID = DEFAULT_SKIN, Name = "osu!lazer", - Creator = "team osu!" + Creator = "team osu!", + InstantiationInfo = typeof(DefaultSkin).AssemblyQualifiedName, }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index f9e22c9c89..2ea236e44f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning private readonly IResourceStore legacyDefaultResources; - public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin()); + public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; public override IEnumerable HandledExtensions => new[] { ".osk" }; @@ -108,7 +108,7 @@ namespace osu.Game.Skinning { // we need to populate early to create a hash based off skin.ini contents if (item.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(item); + populateMetadata(item, GetSkin(item)); if (item.Creator != null && item.Creator != unknown_creator_string) { @@ -125,18 +125,20 @@ namespace osu.Game.Skinning { await base.Populate(model, archive, cancellationToken).ConfigureAwait(false); + var instance = GetSkin(model); + + model.InstantiationInfo ??= instance.GetType().AssemblyQualifiedName; + if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(model); + populateMetadata(model, instance); } - private void populateMetadata(SkinInfo item) + private void populateMetadata(SkinInfo item, Skin instance) { - Skin reference = GetSkin(item); - - if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) + if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) { - item.Name = reference.Configuration.SkinInfo.Name; - item.Creator = reference.Configuration.SkinInfo.Creator; + item.Name = instance.Configuration.SkinInfo.Name; + item.Creator = instance.Configuration.SkinInfo.Creator; } else { @@ -150,7 +152,7 @@ namespace osu.Game.Skinning /// /// The skin to lookup. /// A instance correlating to the provided . - public Skin GetSkin(SkinInfo skinInfo) + public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this); { if (skinInfo == SkinInfo.Default) return new DefaultSkin(); From a7e83aacfb2b01201f7f8d87f584fbc871c30b3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:00:56 +0900 Subject: [PATCH 061/215] Ensure default skins are copied before modifying --- osu.Game/Skinning/Editor/SkinEditor.cs | 30 ++++++++++++-------------- osu.Game/Skinning/SkinManager.cs | 26 ++++++++++++++-------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 6b7d289284..90b003153b 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -30,6 +31,8 @@ namespace osu.Game.Skinning.Editor [Resolved] private SkinManager skins { get; set; } + private Bindable currentSkin; + public SkinEditor(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -119,23 +122,25 @@ namespace osu.Game.Skinning.Editor protected override void LoadComplete() { base.LoadComplete(); + Show(); + + // as long as the skin editor is loaded, let's make sure we can modify the current skin. + currentSkin = skins.CurrentSkin.GetBoundCopy(); + + // schedule ensures this only happens when the skin editor is visible. + // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). + // probably something which will be factored out in a future database refactor so not too concerning for now. + currentSkin.BindValueChanged(skin => Scheduler.AddOnce(skins.EnsureMutableSkin), true); } private void revert() { - var currentSkin = skins.CurrentSkin.Value; - - var legacySkin = currentSkin as LegacySkin; - - if (legacySkin == null) - return; - SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) { - legacySkin.ResetDrawableTarget(t); + currentSkin.Value.ResetDrawableTarget(t); // add back default components getTarget(t.Target).Reload(); @@ -144,17 +149,10 @@ namespace osu.Game.Skinning.Editor private void save() { - var currentSkin = skins.CurrentSkin.Value; - - var legacySkin = currentSkin as LegacySkin; - - if (legacySkin == null) - return; - SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) - legacySkin.UpdateDrawableTarget(t); + currentSkin.Value.UpdateDrawableTarget(t); skins.Save(skins.CurrentSkin.Value); } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2ea236e44f..63d7e4f86f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -153,22 +153,30 @@ namespace osu.Game.Skinning /// The skin to lookup. /// A instance correlating to the provided . public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this); + + /// + /// Ensure that the current skin is in a state it can accept user modifications. + /// This will create a copy of any internal skin and being tracking in the database if not already. + /// + public void EnsureMutableSkin() { - if (skinInfo == SkinInfo.Default) - return new DefaultSkin(); + if (CurrentSkinInfo.Value.ID >= 1) return; - if (skinInfo == DefaultLegacySkin.Info) - return new DefaultLegacySkin(legacyDefaultResources, this); + var skin = CurrentSkin.Value; - return new LegacySkin(skinInfo, this); + // if the user is attempting to save one of the default skin implementations, create a copy first. + CurrentSkinInfo.Value = Import(new SkinInfo + { + Name = skin.SkinInfo.Name + " (modified)", + Creator = skin.SkinInfo.Creator, + InstantiationInfo = skin.SkinInfo.InstantiationInfo, + }).Result; } public void Save(Skin skin) { - // some skins don't support saving just yet. - // eventually we will want to create a copy of the skin to allow for customisation. - if (skin.SkinInfo.Files == null) - return; + if (skin.SkinInfo.ID <= 0) + throw new InvalidOperationException($"Attempting to save a skin which is not yet tracked. Call {nameof(EnsureMutableSkin)} first."); foreach (var drawableInfo in skin.DrawableComponentInfo) { From 61ea3f2e6410980341803eb2c4548e917229ee3e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:08:32 +0900 Subject: [PATCH 062/215] Remove unnecessary test step creating needless skins --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index b0edc0dd68..73da76df78 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -22,13 +22,6 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public override void SetUpSteps() { - AddStep("set empty legacy skin", () => - { - var imported = skinManager.Import(new SkinInfo { Name = "test skin" }).Result; - - skinManager.CurrentSkinInfo.Value = imported; - }); - base.SetUpSteps(); AddStep("reload skin editor", () => From a88a8b7d8d504cf981bbcd65c1d81ba9a69a6320 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:48:08 +0900 Subject: [PATCH 063/215] Use `ISkinnableComponent` wherever possible (and expose as `BindableList`) --- .../HUD/SkinnableElementTargetContainer.cs | 32 +++++++++++++++---- osu.Game/Screens/Play/HUDOverlay.cs | 32 +++++++++---------- osu.Game/Skinning/ISkinnableTarget.cs | 4 +-- osu.Game/Skinning/SkinnableTargetWrapper.cs | 2 +- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index 051ab9ad3c..b2f6d32927 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -1,8 +1,10 @@ // 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.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Extensions; using osu.Game.Skinning; @@ -15,30 +17,46 @@ namespace osu.Game.Screens.Play.HUD public SkinnableTarget Target { get; } + public IBindableList Components => components; + + private readonly BindableList components = new BindableList(); + public SkinnableElementTargetContainer(SkinnableTarget target) { Target = target; } - public IReadOnlyList Children => content?.Children; - public void Reload() { + ClearInternal(); + components.Clear(); + content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; - ClearInternal(); - if (content != null) - LoadComponentAsync(content, AddInternal); + { + LoadComponentAsync(content, wrapper => + { + AddInternal(wrapper); + components.AddRange(wrapper.Children.OfType()); + }); + } } - public void Add(Drawable drawable) + public void Add(ISkinnableComponent component) { + if (content == null) + throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); + + if (!(component is Drawable drawable)) + throw new ArgumentException("Provided argument must be of type {nameof(ISkinnableComponent)}.", nameof(drawable)); + content.Add(drawable); + components.Add(component); } public IEnumerable CreateSerialisedChildren() => - content.Select(d => d.CreateSerialisedInformation()); + components.Select(d => ((Drawable)d).CreateSerialisedInformation()); protected override void SkinChanged(ISkinSource skin, bool allowFallback) { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index c4a8860bb6..887346b5df 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; @@ -207,27 +208,24 @@ namespace osu.Game.Screens.Play Vector2 lowestScreenSpace = Vector2.Zero; - // TODO: may be null during skin switching. not sure if there's a better way of exposing these children. - if (mainComponents.Children != null) + // LINQ cast can be removed when IDrawable interface includes Anchor / RelativeSizeAxes. + foreach (var element in mainComponents.Components.Cast()) { - foreach (var element in mainComponents.Children) - { - // for now align top-right components with the bottom-edge of the lowest top-anchored hud element. - if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X)) - continue; + // for now align top-right components with the bottom-edge of the lowest top-anchored hud element. + if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X)) + continue; - // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. - if (element is LegacyHealthDisplay) - continue; + // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. + if (element is LegacyHealthDisplay) + continue; - var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; - if (bottomRight.Y > lowestScreenSpace.Y) - lowestScreenSpace = bottomRight; - } - - topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; - bottomRightElements.Y = -Progress.Height; + var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; + if (bottomRight.Y > lowestScreenSpace.Y) + lowestScreenSpace = bottomRight; } + + topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; + bottomRightElements.Y = -Progress.Height; } private void updateVisibility() diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 2379fd4247..4d97b42e8e 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -1,8 +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 osu.Framework.Graphics; - namespace osu.Game.Skinning { /// @@ -20,6 +18,6 @@ namespace osu.Game.Skinning /// /// Add the provided item to this target. /// - public void Add(Drawable drawable); + public void Add(ISkinnableComponent drawable); } } diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index a0df610488..0d16775f51 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { /// - /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type. + /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type (for consumption via . /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// public class SkinnableTargetWrapper : Container, ISkinSerialisable From a4e052961770c3eb98408393ad198287ebcb0766 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:49:00 +0900 Subject: [PATCH 064/215] Replace polling logic with direct bindable reactions --- .../Compose/Components/BlueprintContainer.cs | 23 +++++++++ .../Components/EditorBlueprintContainer.cs | 22 +-------- .../Skinning/Editor/SkinBlueprintContainer.cs | 49 ++++++++++++++++--- osu.Game/Skinning/Editor/SkinEditor.cs | 15 +++++- 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 361e98e0dd..edd1acbd6c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -24,6 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Includes selection and manipulation support via a . /// public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler + where T : class { protected DragBox DragBox { get; private set; } @@ -39,6 +42,8 @@ namespace osu.Game.Screens.Edit.Compose.Components [Resolved(CanBeNull = true)] private IEditorChangeHandler changeHandler { get; set; } + protected readonly BindableList SelectedItems = new BindableList(); + protected BlueprintContainer() { RelativeSizeAxes = Axes.Both; @@ -47,6 +52,24 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { + SelectedItems.CollectionChanged += (selectedObjects, args) => + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var o in args.NewItems) + SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select(); + + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var o in args.OldItems) + SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect(); + + break; + } + }; + SelectionHandler = CreateSelectionHandler(); SelectionHandler.DeselectAll = deselectAll; diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index db322faf65..31a191c80c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -2,10 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -24,8 +22,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected readonly HitObjectComposer Composer; - private readonly BindableList selectedHitObjects = new BindableList(); - protected EditorBlueprintContainer(HitObjectComposer composer) { Composer = composer; @@ -34,23 +30,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - selectedHitObjects.BindTo(Beatmap.SelectedHitObjects); - selectedHitObjects.CollectionChanged += (selectedObjects, args) => - { - switch (args.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (var o in args.NewItems) - SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select(); - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var o in args.OldItems) - SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect(); - - break; - } - }; + SelectedItems.BindTo(Beatmap.SelectedHitObjects); } protected override void LoadComplete() diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 45e541abf0..bc1ea02090 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -1,11 +1,16 @@ // 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.Collections.Specialized; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning.Editor { @@ -18,23 +23,51 @@ namespace osu.Game.Skinning.Editor this.target = target; } + [BackgroundDependencyLoader(true)] + private void load(SkinEditor editor) + { + SelectedItems.BindTo(editor.SelectedComponents); + } + + private readonly List> targetComponents = new List>(); + protected override void LoadComplete() { base.LoadComplete(); - checkForComponents(); + // track each target container on the current screen. + foreach (var targetContainer in target.ChildrenOfType()) + { + var bindableList = new BindableList { BindTarget = targetContainer.Components }; + bindableList.BindCollectionChanged(componentsChanged, true); + + targetComponents.Add(bindableList); + } } - private void checkForComponents() + private void componentsChanged(object sender, NotifyCollectionChangedEventArgs e) { - ISkinnableComponent[] skinnableComponents = target.ChildrenOfType().ToArray(); + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var item in e.NewItems.Cast()) + AddBlueprintFor(item); + break; - foreach (var c in skinnableComponents) - AddBlueprintFor(c); + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Reset: + foreach (var item in e.OldItems.Cast()) + RemoveBlueprintFor(item); + break; - // We'd hope to eventually be running this in a more sensible way, but this handles situations where new drawables become present (ie. during ongoing gameplay) - // or when drawables in the target are loaded asynchronously and may not be immediately available when this BlueprintContainer is loaded. - Scheduler.AddDelayed(checkForComponents, 1000); + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems.Cast()) + RemoveBlueprintFor(item); + + foreach (var item in e.NewItems.Cast()) + AddBlueprintFor(item); + break; + } } protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 90b003153b..b098136eda 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -18,6 +18,7 @@ using osuTK; namespace osu.Game.Skinning.Editor { + [Cached(typeof(SkinEditor))] public class SkinEditor : FocusedOverlayContainer { public const double TRANSITION_DURATION = 500; @@ -28,11 +29,15 @@ namespace osu.Game.Skinning.Editor protected override bool StartHidden => true; + public readonly BindableList SelectedComponents = new BindableList(); + [Resolved] private SkinManager skins { get; set; } private Bindable currentSkin; + private SkinBlueprintContainer blueprintContainer; + public SkinEditor(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -56,7 +61,7 @@ namespace osu.Game.Skinning.Editor Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X }, - new SkinBlueprintContainer(targetScreen), + blueprintContainer = new SkinBlueprintContainer(targetScreen), new SkinComponentToolbox(600) { Anchor = Anchor.CentreLeft, @@ -109,9 +114,15 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { - Drawable instance = (Drawable)Activator.CreateInstance(type); + var instance = (Drawable)Activator.CreateInstance(type) as ISkinnableComponent; + + if (instance == null) + throw new InvalidOperationException("Attempted to instantiate a component for placement which was not an {typeof(ISkinnableComponent)}."); getTarget(SkinnableTarget.MainHUDComponents)?.Add(instance); + + SelectedComponents.Clear(); + SelectedComponents.Add(instance); } private ISkinnableTarget getTarget(SkinnableTarget target) From 1831f581aa15f14f4cd01bf73122dedc5a5d804b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 18:07:58 +0900 Subject: [PATCH 065/215] Add basic metadata display and remove outdated message about not saving --- osu.Game/Skinning/Editor/SkinEditor.cs | 55 ++++++++++++++++---------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index b098136eda..0e243d1a02 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -36,7 +36,8 @@ namespace osu.Game.Skinning.Editor private Bindable currentSkin; - private SkinBlueprintContainer blueprintContainer; + [Resolved] + private OsuColour colours { get; set; } public SkinEditor(Drawable targetScreen) { @@ -46,7 +47,7 @@ namespace osu.Game.Skinning.Editor } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { InternalChild = new OsuContextMenuContainer { @@ -61,7 +62,7 @@ namespace osu.Game.Skinning.Editor Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X }, - blueprintContainer = new SkinBlueprintContainer(targetScreen), + new SkinBlueprintContainer(targetScreen), new SkinComponentToolbox(600) { Anchor = Anchor.CentreLeft, @@ -103,13 +104,42 @@ namespace osu.Game.Skinning.Editor }, } }; + } - headerText.AddParagraph("Skin editor (preview)", cp => cp.Font = OsuFont.Default.With(size: 24)); - headerText.AddParagraph("This is a preview of what is to come. Changes are lost on changing screens.", cp => + protected override void LoadComplete() + { + base.LoadComplete(); + + Show(); + + // as long as the skin editor is loaded, let's make sure we can modify the current skin. + currentSkin = skins.CurrentSkin.GetBoundCopy(); + + // schedule ensures this only happens when the skin editor is visible. + // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). + // probably something which will be factored out in a future database refactor so not too concerning for now. + currentSkin.BindValueChanged(skin => Scheduler.AddOnce(skinChanged), true); + } + + private void skinChanged() + { + headerText.Clear(); + + headerText.AddParagraph("Skin editor", cp => cp.Font = OsuFont.Default.With(size: 24)); + headerText.NewParagraph(); + headerText.AddText("Currently editing ", cp => { cp.Font = OsuFont.Default.With(size: 12); cp.Colour = colours.Yellow; }); + + headerText.AddText($"{currentSkin.Value.SkinInfo}", cp => + { + cp.Font = OsuFont.Default.With(size: 12, weight: FontWeight.Bold); + cp.Colour = colours.Yellow; + }); + + skins.EnsureMutableSkin(); } private void placeComponent(Type type) @@ -130,21 +160,6 @@ namespace osu.Game.Skinning.Editor return targetScreen.ChildrenOfType().FirstOrDefault(c => c.Target == target); } - protected override void LoadComplete() - { - base.LoadComplete(); - - Show(); - - // as long as the skin editor is loaded, let's make sure we can modify the current skin. - currentSkin = skins.CurrentSkin.GetBoundCopy(); - - // schedule ensures this only happens when the skin editor is visible. - // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). - // probably something which will be factored out in a future database refactor so not too concerning for now. - currentSkin.BindValueChanged(skin => Scheduler.AddOnce(skins.EnsureMutableSkin), true); - } - private void revert() { SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); From 6d587dc39229c8150e6f563ec5122b81115f5edc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 18:17:05 +0900 Subject: [PATCH 066/215] Adjust target size slightly to better align with the screen --- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index cc989bb459..cbed498a38 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -71,7 +71,7 @@ namespace osu.Game.Skinning.Editor target.RelativePositionAxes = Axes.Both; target.ScaleTo(VISIBLE_TARGET_SCALE, SkinEditor.TRANSITION_DURATION, Easing.OutQuint); - target.MoveToX(0.1f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint); + target.MoveToX(0.095f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint); } else { From f55407f871a94a5846270f947b1326ecdb9de8bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 18:37:41 +0900 Subject: [PATCH 067/215] Show a message when attempting to customisse a screen which doesn't support it --- osu.Game/Screens/ScreenWhiteBox.cs | 24 ++--- .../Skinning/Editor/SkinBlueprintContainer.cs | 14 ++- osu.Game/Skinning/Editor/SkinEditor.cs | 90 ++++++++++++------- 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/osu.Game/Screens/ScreenWhiteBox.cs b/osu.Game/Screens/ScreenWhiteBox.cs index 3d8fd5dad7..cf0c183766 100644 --- a/osu.Game/Screens/ScreenWhiteBox.cs +++ b/osu.Game/Screens/ScreenWhiteBox.cs @@ -87,9 +87,9 @@ namespace osu.Game.Screens private static Color4 getColourFor(object type) { int hash = type.GetHashCode(); - byte r = (byte)Math.Clamp(((hash & 0xFF0000) >> 16) * 0.8f, 20, 255); - byte g = (byte)Math.Clamp(((hash & 0x00FF00) >> 8) * 0.8f, 20, 255); - byte b = (byte)Math.Clamp((hash & 0x0000FF) * 0.8f, 20, 255); + byte r = (byte)Math.Clamp(((hash & 0xFF0000) >> 16) * 2, 128, 255); + byte g = (byte)Math.Clamp(((hash & 0x00FF00) >> 8) * 2, 128, 255); + byte b = (byte)Math.Clamp((hash & 0x0000FF) * 2, 128, 255); return new Color4(r, g, b, 255); } @@ -109,10 +109,10 @@ namespace osu.Game.Screens private readonly Container boxContainer; - public UnderConstructionMessage(string name) + public UnderConstructionMessage(string name, string description = "is not yet ready for use!") { - RelativeSizeAxes = Axes.Both; - Size = new Vector2(0.3f); + AutoSizeAxes = Axes.Both; + Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -124,7 +124,7 @@ namespace osu.Game.Screens { CornerRadius = 20, Masking = true, - RelativeSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] @@ -133,15 +133,15 @@ namespace osu.Game.Screens { RelativeSizeAxes = Axes.Both, - Colour = colour, - Alpha = 0.2f, - Blending = BlendingParameters.Additive, + Colour = colour.Darken(0.8f), + Alpha = 0.8f, }, TextContainer = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, + Padding = new MarginPadding(20), Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -157,14 +157,14 @@ namespace osu.Game.Screens Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Text = name, - Colour = colour.Lighten(0.8f), + Colour = colour, Font = OsuFont.GetFont(size: 36), }, new OsuSpriteText { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "is not yet ready for use!", + Text = description, Font = OsuFont.GetFont(size: 20), }, new OsuSpriteText diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index bc1ea02090..ef189ed165 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -7,8 +7,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Rulesets.Edit; +using osu.Game.Screens; using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Play.HUD; @@ -36,7 +38,17 @@ namespace osu.Game.Skinning.Editor base.LoadComplete(); // track each target container on the current screen. - foreach (var targetContainer in target.ChildrenOfType()) + var targetContainers = target.ChildrenOfType().ToArray(); + + if (targetContainers.Length == 0) + { + var targetScreen = target.ChildrenOfType().LastOrDefault()?.GetType().Name ?? "this screen"; + + AddInternal(new ScreenWhiteBox.UnderConstructionMessage(targetScreen, "doesn't support skin customisation just yet.")); + return; + } + + foreach (var targetContainer in targetContainers) { var bindableList = new BindableList { BindTarget = targetContainer.Components }; bindableList.BindCollectionChanged(componentsChanged, true); diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 0e243d1a02..08fc458b94 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -62,46 +62,68 @@ namespace osu.Game.Skinning.Editor Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X }, - new SkinBlueprintContainer(targetScreen), - new SkinComponentToolbox(600) + new GridContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RequestPlacement = placeComponent - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Spacing = new Vector2(5), - Padding = new MarginPadding + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - Top = 10, - Left = 10, + new Dimension(GridSizeMode.AutoSize), + new Dimension() }, - Margin = new MarginPadding + Content = new[] { - Right = 10, - Bottom = 10, - }, - Children = new Drawable[] - { - new TriangleButton + new Drawable[] { - Text = "Save Changes", - Width = 140, - Action = save, - }, - new DangerousTriangleButton - { - Text = "Revert to default", - Width = 140, - Action = revert, - }, + new SkinComponentToolbox(600) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RequestPlacement = placeComponent + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SkinBlueprintContainer(targetScreen), + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Spacing = new Vector2(5), + Padding = new MarginPadding + { + Top = 10, + Left = 10, + }, + Margin = new MarginPadding + { + Right = 10, + Bottom = 10, + }, + Children = new Drawable[] + { + new TriangleButton + { + Text = "Save Changes", + Width = 140, + Action = save, + }, + new DangerousTriangleButton + { + Text = "Revert to default", + Width = 140, + Action = revert, + }, + } + }, + } + }, + } } - }, + } } }; } From 4bb933e4b111439ac022ed82c713b0197845c469 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 18:55:45 +0900 Subject: [PATCH 068/215] Add missing base lookup call to `DefaultSkin` --- osu.Game/Skinning/DefaultSkin.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 1c063b6ef2..3de3dc7702 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -36,6 +36,9 @@ namespace osu.Game.Skinning public override Drawable GetDrawableComponent(ISkinComponent component) { + if (base.GetDrawableComponent(component) is Drawable c) + return c; + switch (component) { case SkinnableTargetComponent target: From 1231c08a0791ec9b739303f29ce3bc2aa9a36906 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 18:58:26 +0900 Subject: [PATCH 069/215] Rename mismatching file --- .../{IImmutableSkinnableComponent.cs => ISkinSerialisable.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Skinning/{IImmutableSkinnableComponent.cs => ISkinSerialisable.cs} (100%) diff --git a/osu.Game/Skinning/IImmutableSkinnableComponent.cs b/osu.Game/Skinning/ISkinSerialisable.cs similarity index 100% rename from osu.Game/Skinning/IImmutableSkinnableComponent.cs rename to osu.Game/Skinning/ISkinSerialisable.cs From 77e422409cdc9e6b216724f778c2b32617878ebf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 May 2021 17:00:24 +0900 Subject: [PATCH 070/215] Add `SkinInfo.InstantiationInfo` to allow creating different skin types --- .../TestSceneHitCircleArea.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- ...60743_AddSkinInstantiationInfo.Designer.cs | 508 ++++++++++++++++++ ...20210511060743_AddSkinInstantiationInfo.cs | 22 + .../Migrations/OsuDbContextModelSnapshot.cs | 6 +- osu.Game/Skinning/DefaultLegacySkin.cs | 10 +- osu.Game/Skinning/DefaultSkin.cs | 10 +- osu.Game/Skinning/SkinInfo.cs | 36 +- osu.Game/Skinning/SkinManager.cs | 31 +- 9 files changed, 597 insertions(+), 30 deletions(-) create mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs create mode 100644 osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs index 0649989dc0..1fdcd73dde 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - Child = new SkinProvidingContainer(new DefaultSkin()) + Child = new SkinProvidingContainer(new DefaultSkin(null)) { RelativeSizeAxes = Axes.Both, Child = drawableHitCircle = new DrawableHitCircle(hitCircle) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index e0eeaf6db0..3576b149bf 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -324,7 +324,7 @@ namespace osu.Game.Beatmaps public bool SkinLoaded => skin.IsResultAvailable; public ISkin Skin => skin.Value; - protected virtual ISkin GetSkin() => new DefaultSkin(); + protected virtual ISkin GetSkin() => new DefaultSkin(null); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs new file mode 100644 index 0000000000..b808c648da --- /dev/null +++ b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.Designer.cs @@ -0,0 +1,508 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210511060743_AddSkinInstantiationInfo")] + partial class AddSkinInstantiationInfo + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("EpilepsyWarning"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs new file mode 100644 index 0000000000..1d5b0769a4 --- /dev/null +++ b/osu.Game/Migrations/20210511060743_AddSkinInstantiationInfo.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddSkinInstantiationInfo : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "InstantiationInfo", + table: "SkinInfo", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "InstantiationInfo", + table: "SkinInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index ec4461ca56..d4bde50b60 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -141,8 +141,6 @@ namespace osu.Game.Migrations b.Property("TitleUnicode"); - b.Property("VideoFile"); - b.HasKey("ID"); b.ToTable("BeatmapMetadata"); @@ -352,7 +350,7 @@ namespace osu.Game.Migrations b.Property("TotalScore"); - b.Property("UserID") + b.Property("UserID") .HasColumnName("UserID"); b.Property("UserString") @@ -402,6 +400,8 @@ namespace osu.Game.Migrations b.Property("Hash"); + b.Property("InstantiationInfo"); + b.Property("Name"); b.HasKey("ID"); diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 4027cc650d..564be8630e 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -10,7 +10,12 @@ namespace osu.Game.Skinning public class DefaultLegacySkin : LegacySkin { public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) - : base(Info, storage, resources, string.Empty) + : this(Info, storage, resources) + { + } + + public DefaultLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources) + : base(skin, storage, resources, string.Empty) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.AddComboColours( @@ -27,7 +32,8 @@ namespace osu.Game.Skinning { ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. Name = "osu!classic", - Creator = "team osu!" + Creator = "team osu!", + InstantiationInfo = typeof(DefaultLegacySkin).AssemblyQualifiedName, }; } } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 0b3f5f3cde..fcd874d6ca 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -8,14 +8,20 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; +using osu.Game.IO; using osuTK.Graphics; namespace osu.Game.Skinning { public class DefaultSkin : Skin { - public DefaultSkin() - : base(SkinInfo.Default) + public DefaultSkin(IStorageResourceProvider resources) + : this(SkinInfo.Default, resources) + { + } + + public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources) + : base(skin) { Configuration = new DefaultSkinConfiguration(); } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index aaccbefb3d..2e29808cb4 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.IO; namespace osu.Game.Skinning { @@ -22,7 +25,35 @@ namespace osu.Game.Skinning public string Creator { get; set; } - public List Files { get; set; } + private string instantiationInfo; + + public string InstantiationInfo + { + get => instantiationInfo; + set => instantiationInfo = abbreviateInstantiationInfo(value); + } + + private string abbreviateInstantiationInfo(string value) + { + // exclude version onwards, matching only on namespace and type. + // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. + return string.Join(',', value.Split(',').Take(2)); + } + + public virtual Skin CreateInstance(IResourceStore legacyDefaultResources, IStorageResourceProvider resources) + { + var type = string.IsNullOrEmpty(InstantiationInfo) + // handle the case of skins imported before InstantiationInfo was added. + ? typeof(LegacySkin) + : Type.GetType(InstantiationInfo); + + if (type == typeof(DefaultLegacySkin)) + return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources); + + return (Skin)Activator.CreateInstance(type, this, resources); + } + + public List Files { get; set; } = new List(); public List Settings { get; set; } @@ -32,7 +63,8 @@ namespace osu.Game.Skinning { ID = DEFAULT_SKIN, Name = "osu!lazer", - Creator = "team osu!" + Creator = "team osu!", + InstantiationInfo = typeof(DefaultSkin).AssemblyQualifiedName, }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ac4d63159a..dbb9dcb7fc 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -36,7 +36,7 @@ namespace osu.Game.Skinning private readonly IResourceStore legacyDefaultResources; - public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin()); + public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin(null)); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; public override IEnumerable HandledExtensions => new[] { ".osk" }; @@ -105,7 +105,7 @@ namespace osu.Game.Skinning { // we need to populate early to create a hash based off skin.ini contents if (item.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(item); + populateMetadata(item, GetSkin(item)); if (item.Creator != null && item.Creator != unknown_creator_string) { @@ -122,18 +122,20 @@ namespace osu.Game.Skinning { await base.Populate(model, archive, cancellationToken).ConfigureAwait(false); + var instance = GetSkin(model); + + model.InstantiationInfo ??= instance.GetType().AssemblyQualifiedName; + if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) - populateMetadata(model); + populateMetadata(model, instance); } - private void populateMetadata(SkinInfo item) + private void populateMetadata(SkinInfo item, Skin instance) { - Skin reference = GetSkin(item); - - if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name)) + if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) { - item.Name = reference.Configuration.SkinInfo.Name; - item.Creator = reference.Configuration.SkinInfo.Creator; + item.Name = instance.Configuration.SkinInfo.Name; + item.Creator = instance.Configuration.SkinInfo.Creator; } else { @@ -147,16 +149,7 @@ namespace osu.Game.Skinning /// /// The skin to lookup. /// A instance correlating to the provided . - public Skin GetSkin(SkinInfo skinInfo) - { - if (skinInfo == SkinInfo.Default) - return new DefaultSkin(); - - if (skinInfo == DefaultLegacySkin.Info) - return new DefaultLegacySkin(legacyDefaultResources, this); - - return new LegacySkin(skinInfo, this); - } + public Skin GetSkin(SkinInfo skinInfo) => skinInfo.CreateInstance(legacyDefaultResources, this); /// /// Perform a lookup query on available s. From 97bd482d4dae632548438ec05315ac37aec8c8fa Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:21:38 +0200 Subject: [PATCH 071/215] Factor out `load` from settings into new `Settings` class --- osu.Game/Screens/Edit/Settings.cs | 44 +++++++++++++++++++ .../Edit/Timing/ControlPointSettings.cs | 35 +-------------- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 32 +------------- 3 files changed, 48 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Screens/Edit/Settings.cs diff --git a/osu.Game/Screens/Edit/Settings.cs b/osu.Game/Screens/Edit/Settings.cs new file mode 100644 index 0000000000..758414333d --- /dev/null +++ b/osu.Game/Screens/Edit/Settings.cs @@ -0,0 +1,44 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays; + +namespace osu.Game.Screens.Edit +{ + public abstract class Settings : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Background4, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = CreateSections() + }, + } + }; + } + + protected abstract IReadOnlyList CreateSections(); + } +} diff --git a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs index 921fa675b3..36f31d4be4 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs @@ -2,44 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Containers; -using osu.Game.Overlays; namespace osu.Game.Screens.Edit.Timing { - public class ControlPointSettings : CompositeDrawable + public class ControlPointSettings : Settings { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) - { - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colours.Background4, - RelativeSizeAxes = Axes.Both, - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = createSections() - }, - } - }; - } - - private IReadOnlyList createSections() => new Drawable[] + protected override IReadOnlyList CreateSections() => new Drawable[] { new GroupSection(), new TimingSection(), diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 4519231cd2..15fc54d64f 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -2,44 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Edit.Verify { - public class IssueSettings : CompositeDrawable + public class IssueSettings : Settings { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - RelativeSizeAxes = Axes.Both; - InternalChildren = new Drawable[] - { - new Box - { - Colour = colours.Gray3, - RelativeSizeAxes = Axes.Both, - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = createSections() - }, - } - }; } - private IReadOnlyList createSections() => new Drawable[] + protected override IReadOnlyList CreateSections() => new Drawable[] { }; } From d3c1ec55eec7399ea8405d1f39ee92a6f9b2fca6 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:22:32 +0200 Subject: [PATCH 072/215] Take `IssueList` in `IssueSettings` constructor We'll be using this for bindables later. --- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 4 ++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 15fc54d64f..be06700b28 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -8,7 +8,11 @@ namespace osu.Game.Screens.Edit.Verify { public class IssueSettings : Settings { + private readonly IssueList issueList; + public IssueSettings(IssueList issueList) + { + this.issueList = issueList; } protected override IReadOnlyList CreateSections() => new Drawable[] diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 9de1f04271..9fb81a6681 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -30,6 +30,8 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load() { + IssueList issueList; + Child = new Container { RelativeSizeAxes = Axes.Both, @@ -45,8 +47,8 @@ namespace osu.Game.Screens.Edit.Verify { new Drawable[] { - new IssueList(), - new IssueSettings(), + issueList = new IssueList(), + new IssueSettings(issueList), }, } } From 1de35f880b8b571e7aa89c597c69a8dcd1dd8dde Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:23:31 +0200 Subject: [PATCH 073/215] Separate `IssueList` into own class --- osu.Game/Screens/Edit/Verify/IssueList.cs | 100 +++++++++++++++++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 88 ---------------- 2 files changed, 100 insertions(+), 88 deletions(-) create mode 100644 osu.Game/Screens/Edit/Verify/IssueList.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs new file mode 100644 index 0000000000..6d319eb09e --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -0,0 +1,100 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; +using osuTK; + +namespace osu.Game.Screens.Edit.Verify +{ + public class IssueList : CompositeDrawable + { + private IssueTable table; + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private IBindable workingBeatmap { get; set; } + + [Resolved] + private EditorBeatmap beatmap { get; set; } + + [Resolved] + private Bindable selectedIssue { get; set; } + + private IBeatmapVerifier rulesetVerifier; + private BeatmapVerifier generalVerifier; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) + { + generalVerifier = new BeatmapVerifier(); + rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); + + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Background2, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = table = new IssueTable(), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding(20), + Children = new Drawable[] + { + new TriangleButton + { + Text = "Refresh", + Action = refresh, + Size = new Vector2(120, 40), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + } + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + refresh(); + } + + private void refresh() + { + var issues = generalVerifier.Run(beatmap, workingBeatmap.Value); + + if (rulesetVerifier != null) + issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value)); + + table.Issues = issues + .OrderBy(issue => issue.Template.Type) + .ThenBy(issue => issue.Check.Metadata.Category); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 9fb81a6681..ed4658da8e 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -1,19 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; -using osuTK; namespace osu.Game.Screens.Edit.Verify { @@ -54,85 +46,5 @@ namespace osu.Game.Screens.Edit.Verify } }; } - - public class IssueList : CompositeDrawable - { - private IssueTable table; - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private IBindable workingBeatmap { get; set; } - - [Resolved] - private EditorBeatmap beatmap { get; set; } - - [Resolved] - private Bindable selectedIssue { get; set; } - - private IBeatmapVerifier rulesetVerifier; - private BeatmapVerifier generalVerifier; - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) - { - generalVerifier = new BeatmapVerifier(); - rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); - - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colours.Background2, - RelativeSizeAxes = Axes.Both, - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - Child = table = new IssueTable(), - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Margin = new MarginPadding(20), - Children = new Drawable[] - { - new TriangleButton - { - Text = "Refresh", - Action = refresh, - Size = new Vector2(120, 40), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - }, - } - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - refresh(); - } - - private void refresh() - { - var issues = generalVerifier.Run(beatmap, workingBeatmap.Value); - - if (rulesetVerifier != null) - issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value)); - - table.Issues = issues - .OrderBy(issue => issue.Template.Type) - .ThenBy(issue => issue.Check.Metadata.Category); - } - } } } From 01b87947579c417395458c2832ba730d331d6e6b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:26:12 +0200 Subject: [PATCH 074/215] Add abstract `Section` class Similar to `Section` in the timing screen, but does not make use of checkboxes, nor specific to control points. So there's a lot of things that differ, hence new class instead of factoring that out. --- osu.Game/Screens/Edit/Verify/Section.cs | 67 +++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 osu.Game/Screens/Edit/Verify/Section.cs diff --git a/osu.Game/Screens/Edit/Verify/Section.cs b/osu.Game/Screens/Edit/Verify/Section.cs new file mode 100644 index 0000000000..f6815a05a1 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Section.cs @@ -0,0 +1,67 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.Edit.Verify +{ + public abstract class Section : CompositeDrawable + { + private const int header_height = 50; + + protected readonly IssueList IssueList; + + protected FillFlowContainer Flow; + protected abstract string Header { get; } + + protected Section(IssueList issueList) + { + IssueList = issueList; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Masking = true; + + InternalChildren = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Height = header_height, + Padding = new MarginPadding { Horizontal = 20 }, + Child = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = Header, + Font = new FontUsage(size: 25, weight: "bold") + } + }, + new Container + { + Y = header_height, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = Flow = new FillFlowContainer + { + Padding = new MarginPadding { Horizontal = 20 }, + Spacing = new Vector2(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } + } + }; + } + } +} From 2e4399f0c1f4bcb5cc66bf755f0e4954ab5c6b66 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:27:21 +0200 Subject: [PATCH 075/215] Add `VisibilitySection` and its bindables in `IssueList` --- osu.Game/Screens/Edit/Verify/IssueList.cs | 10 +++++ osu.Game/Screens/Edit/Verify/IssueSettings.cs | 1 + .../Screens/Edit/Verify/VisibilitySection.cs | 38 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 osu.Game/Screens/Edit/Verify/VisibilitySection.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 6d319eb09e..dd1dffcb42 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -34,12 +34,22 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private Bindable selectedIssue { get; set; } + public Dictionary> ShowType { get; set; } + private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { + // Reflects the user interface. Only types in this dictionary have configurable visibility. + ShowType = new Dictionary> + { + { IssueType.Warning, new Bindable(true) }, + { IssueType.Error, new Bindable(true) }, + { IssueType.Negligible, new Bindable(false) } + }; + generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index be06700b28..0df3ab0dcb 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -17,6 +17,7 @@ namespace osu.Game.Screens.Edit.Verify protected override IReadOnlyList CreateSections() => new Drawable[] { + new VisibilitySection(issueList) }; } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs new file mode 100644 index 0000000000..e9fa9b56ed --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -0,0 +1,38 @@ +// 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.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Screens.Edit.Verify +{ + internal class VisibilitySection : Section + { + public VisibilitySection(IssueList issueList) + : base(issueList) + { + } + + protected override string Header => "Visibility"; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) + { + foreach (IssueType issueType in IssueList.ShowType.Keys) + { + var checkbox = new SettingsCheckbox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + LabelText = issueType.ToString() + }; + + checkbox.Current.BindTo(IssueList.ShowType[issueType]); + Flow.Add(checkbox); + } + } + } +} From 1bb7d412daf2ad534dcb3a09161f459fb698e535 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:29:46 +0200 Subject: [PATCH 076/215] Add `IssueList` filtering based on those bindables --- osu.Game/Screens/Edit/Verify/IssueList.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index dd1dffcb42..7a2202c198 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -102,9 +102,22 @@ namespace osu.Game.Screens.Edit.Verify if (rulesetVerifier != null) issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value)); + issues = filter(issues); + table.Issues = issues .OrderBy(issue => issue.Template.Type) .ThenBy(issue => issue.Check.Metadata.Category); } + + private IEnumerable filter(IEnumerable issues) + { + foreach (IssueType issueType in ShowType.Keys) + { + if (!ShowType[issueType].Value) + issues = issues.Where(issue => issue.Template.Type != issueType); + } + + return issues; + } } } From ad78aec1ef8380db80d9e57eb495fa3be2aa2c38 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:30:45 +0200 Subject: [PATCH 077/215] Refresh `IssueList` on changes in `VisibilitySection` --- osu.Game/Screens/Edit/Verify/IssueList.cs | 6 +++--- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 7a2202c198..b7a6d769e3 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -78,7 +78,7 @@ namespace osu.Game.Screens.Edit.Verify new TriangleButton { Text = "Refresh", - Action = refresh, + Action = Refresh, Size = new Vector2(120, 40), Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -92,10 +92,10 @@ namespace osu.Game.Screens.Edit.Verify { base.LoadComplete(); - refresh(); + Refresh(); } - private void refresh() + public void Refresh() { var issues = generalVerifier.Run(beatmap, workingBeatmap.Value); diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index e9fa9b56ed..f849426800 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -31,6 +31,7 @@ namespace osu.Game.Screens.Edit.Verify }; checkbox.Current.BindTo(IssueList.ShowType[issueType]); + checkbox.Current.BindValueChanged(_ => IssueList.Refresh()); Flow.Add(checkbox); } } From 75adec57ebfa8aaefcb124d3c802e2e8f69ee0a4 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:31:16 +0200 Subject: [PATCH 078/215] Remove negligible default hidden TODO --- osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs index 1241e058ad..1f708209fe 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs @@ -18,7 +18,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// An error occurred and a complete check could not be made. Error, - // TODO: Negligible issues should be hidden by default. /// A possible mistake so minor/unlikely that it can often be safely ignored. Negligible, } From 4aeaec6ecc610e4c043881451ff6495edea6bcbe Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 01:32:18 +0200 Subject: [PATCH 079/215] Add `InterpretationSection` and its bindable in `IssueList` We'll eventually connect that bindable so that checks can access it. --- .../Edit/Verify/InterpretationSection.cs | 34 +++++++++++++++++++ osu.Game/Screens/Edit/Verify/IssueList.cs | 4 +++ osu.Game/Screens/Edit/Verify/IssueSettings.cs | 1 + 3 files changed, 39 insertions(+) create mode 100644 osu.Game/Screens/Edit/Verify/InterpretationSection.cs diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs new file mode 100644 index 0000000000..fa0bde9ddc --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -0,0 +1,34 @@ +// 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.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Screens.Edit.Verify +{ + internal class InterpretationSection : Section + { + public InterpretationSection(IssueList issueList) + : base(issueList) + { + } + + protected override string Header => "Interpretation"; + + [BackgroundDependencyLoader] + private void load() + { + var dropdown = new SettingsEnumDropdown + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TooltipText = "Affects checks that depend on difficulty level" + }; + dropdown.Current.BindTo(IssueList.InterpretedDifficulty); + + Flow.Add(dropdown); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index b7a6d769e3..3e836c4010 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Edit.Verify public Dictionary> ShowType { get; set; } + public Bindable InterpretedDifficulty { get; set; } + private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; @@ -53,6 +55,8 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); + InterpretedDifficulty = new Bindable(beatmap.BeatmapInfo.DifficultyRating); + RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 0df3ab0dcb..68ab51c3c8 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -17,6 +17,7 @@ namespace osu.Game.Screens.Edit.Verify protected override IReadOnlyList CreateSections() => new Drawable[] { + new InterpretationSection(issueList), new VisibilitySection(issueList) }; } From c13b93e6f15254845e62046551b5e17802915826 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 02:29:18 +0200 Subject: [PATCH 080/215] Replace `IWorkingBeatmap` arg with `BeatmapVerifierContext` in checks This simplifies passing of contextual information by enabling addition without needing to refactor lots of classes. See next commit for example. --- .../Checks/CheckOffscreenObjectsTest.cs | 10 +++++-- .../Edit/Checks/CheckOffscreenObjects.cs | 3 +- .../Edit/OsuBeatmapVerifier.cs | 4 +-- .../Editing/Checks/CheckAudioQualityTest.cs | 28 ++++++++++------- .../Checks/CheckBackgroundQualityTest.cs | 30 +++++++++++-------- .../Editing/Checks/CheckFilePresenceTest.cs | 10 +++++-- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 4 +-- .../Rulesets/Edit/BeatmapVerifierContext.cs | 25 ++++++++++++++++ .../Rulesets/Edit/Checks/CheckAudioQuality.cs | 4 +-- .../Edit/Checks/CheckBackgroundQuality.cs | 6 ++-- .../Edit/Checks/CheckConcurrentObjects.cs | 2 +- .../Rulesets/Edit/Checks/CheckFilePresence.cs | 2 +- .../Edit/Checks/CheckUnsnappedObjects.cs | 2 +- .../Rulesets/Edit/Checks/Components/ICheck.cs | 4 +-- osu.Game/Rulesets/Edit/IBeatmapVerifier.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueList.cs | 7 +++-- 16 files changed, 96 insertions(+), 47 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 6139b0e676..5545273af2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -224,12 +225,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOk(IBeatmap beatmap) { - Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + Assert.That(check.Run(beatmap, context), Is.Empty); } private void assertOffscreenCircle(IBeatmap beatmap) { - var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList(); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); @@ -237,7 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOffscreenSlider(IBeatmap beatmap) { - var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList(); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 4b0a7531a1..c25539201e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks new IssueTemplateOffscreenSlider(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { foreach (var hitobject in playableBeatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index dab6483179..18faeb4f9f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -17,9 +17,9 @@ namespace osu.Game.Rulesets.Osu.Edit new CheckOffscreenObjects() }; - public IEnumerable Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap)); + return checks.SelectMany(check => check.Run(playableBeatmap, context)); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs index 7658ca728d..fbd02ea54e 100644 --- a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs @@ -6,6 +6,7 @@ using Moq; using NUnit.Framework; using osu.Framework.Audio.Track; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; @@ -40,23 +41,23 @@ namespace osu.Game.Tests.Editing.Checks mock.SetupGet(w => w.Beatmap).Returns(beatmap); mock.SetupGet(w => w.Track).Returns((Track)null); - Assert.That(check.Run(beatmap, mock.Object), Is.Empty); + Assert.That(check.Run(beatmap, new BeatmapVerifierContext(mock.Object)), Is.Empty); } [Test] public void TestAcceptable() { - var mock = getMockWorkingBeatmap(192); + var context = getContext(192); - Assert.That(check.Run(beatmap, mock.Object), Is.Empty); + Assert.That(check.Run(beatmap, context), Is.Empty); } [Test] public void TestNullBitrate() { - var mock = getMockWorkingBeatmap(null); + var context = getContext(null); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate); @@ -65,9 +66,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestZeroBitrate() { - var mock = getMockWorkingBeatmap(0); + var context = getContext(0); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate); @@ -76,9 +77,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestTooHighBitrate() { - var mock = getMockWorkingBeatmap(320); + var context = getContext(320); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooHighBitrate); @@ -87,14 +88,19 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestTooLowBitrate() { - var mock = getMockWorkingBeatmap(64); + var context = getContext(64); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooLowBitrate); } + private BeatmapVerifierContext getContext(int? audioBitrate) + { + return new BeatmapVerifierContext(getMockWorkingBeatmap(audioBitrate).Object); + } + /// /// Returns the mock of the working beatmap with the given audio properties. /// diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs index f0f972d2fa..e96ec5485d 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs @@ -9,6 +9,7 @@ using Moq; using NUnit.Framework; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; using FileInfo = osu.Game.IO.FileInfo; @@ -53,25 +54,25 @@ namespace osu.Game.Tests.Editing.Checks { // While this is a problem, it is out of scope for this check and is caught by a different one. beatmap.Metadata.BackgroundFile = null; - var mock = getMockWorkingBeatmap(null, System.Array.Empty()); + var context = getContext(null, System.Array.Empty()); - Assert.That(check.Run(beatmap, mock.Object), Is.Empty); + Assert.That(check.Run(beatmap, context), Is.Empty); } [Test] public void TestAcceptable() { - var mock = getMockWorkingBeatmap(new Texture(1920, 1080)); + var context = getContext(new Texture(1920, 1080)); - Assert.That(check.Run(beatmap, mock.Object), Is.Empty); + Assert.That(check.Run(beatmap, context), Is.Empty); } [Test] public void TestTooHighResolution() { - var mock = getMockWorkingBeatmap(new Texture(3840, 2160)); + var context = getContext(new Texture(3840, 2160)); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution); @@ -80,9 +81,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestLowResolution() { - var mock = getMockWorkingBeatmap(new Texture(640, 480)); + var context = getContext(new Texture(640, 480)); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution); @@ -91,9 +92,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestTooLowResolution() { - var mock = getMockWorkingBeatmap(new Texture(100, 100)); + var context = getContext(new Texture(100, 100)); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution); @@ -102,14 +103,19 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestTooUncompressed() { - var mock = getMockWorkingBeatmap(new Texture(1920, 1080), new byte[1024 * 1024 * 3]); + var context = getContext(new Texture(1920, 1080), new byte[1024 * 1024 * 3]); - var issues = check.Run(beatmap, mock.Object).ToList(); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooUncompressed); } + private BeatmapVerifierContext getContext(Texture background, [CanBeNull] byte[] fileBytes = null) + { + return new BeatmapVerifierContext(getMockWorkingBeatmap(background, fileBytes).Object); + } + /// /// Returns the mock of the working beatmap with the given background and filesize. /// diff --git a/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs b/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs index f6e875a8fc..424dffcbc2 100644 --- a/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.IO; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; using osu.Game.Tests.Beatmaps; @@ -45,7 +46,8 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestBackgroundSetAndInFiles() { - Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + Assert.That(check.Run(beatmap, context), Is.Empty); } [Test] @@ -53,7 +55,8 @@ namespace osu.Game.Tests.Editing.Checks { beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); - var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList(); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist); @@ -64,7 +67,8 @@ namespace osu.Game.Tests.Editing.Checks { beatmap.Metadata.BackgroundFile = null; - var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList(); + var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); + var issues = check.Run(beatmap, context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet); diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 2f7b7b0ab8..50902d3ff1 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -29,9 +29,9 @@ namespace osu.Game.Rulesets.Edit new CheckConcurrentObjects() }; - public IEnumerable Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap)); + return checks.SelectMany(check => check.Run(playableBeatmap, context)); } } } diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs new file mode 100644 index 0000000000..86ca81491a --- /dev/null +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs @@ -0,0 +1,25 @@ +// 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.Bindables; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// Represents the context provided by the beatmap verifier to the checks it runs. + /// Contains information about what is being checked and how it should be checked. + /// + public class BeatmapVerifierContext + { + /// + /// The working beatmap instance of the current beatmap. + /// + public readonly IWorkingBeatmap WorkingBeatmap; + + public BeatmapVerifierContext(IWorkingBeatmap workingBeatmap) + { + WorkingBeatmap = workingBeatmap; + } + } +} diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs index c1074d7c74..82e572f0b3 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs @@ -26,13 +26,13 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateNoBitrate(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { var audioFile = playableBeatmap.Metadata?.AudioFile; if (audioFile == null) yield break; - var track = workingBeatmap.Track; + var track = context.WorkingBeatmap.Track; if (track?.Bitrate == null || track.Bitrate.Value == 0) yield return new IssueTemplateNoBitrate(this).Create(); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs index 59fee74023..2af1eca340 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs @@ -30,13 +30,13 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateTooUncompressed(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { var backgroundFile = playableBeatmap.Metadata?.BackgroundFile; if (backgroundFile == null) yield break; - var texture = workingBeatmap.Background; + var texture = context.WorkingBeatmap.Background; if (texture == null) yield break; @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit.Checks yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height); string storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); - double filesizeMb = workingBeatmap.GetStream(storagePath).Length / (1024d * 1024d); + double filesizeMb = context.WorkingBeatmap.GetStream(storagePath).Length / (1024d * 1024d); if (filesizeMb > max_filesize_mb) yield return new IssueTemplateTooUncompressed(this).Create(filesizeMb); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs index ddebe2923a..d6e779d147 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateConcurrentDifferent(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { for (int i = 0; i < playableBeatmap.HitObjects.Count - 1; ++i) { diff --git a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs index 006fc57c04..cf990a2106 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateDoesNotExist(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { var filename = GetFilename(playableBeatmap); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs index cdf3f05465..f073915a39 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateSmallUnsnap(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap) + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) { var controlPointInfo = playableBeatmap.ControlPointInfo; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs index 31a7583941..f669e2161c 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// Runs this check and returns any issues detected for the provided beatmap. /// /// The playable beatmap of the beatmap to run the check on. - /// The working beatmap of the beatmap to run the check on. - public IEnumerable Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap); + /// The beatmap verifier context associated with the beatmap. + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context); } } diff --git a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs index b598176a35..4e712351e0 100644 --- a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs @@ -12,6 +12,6 @@ namespace osu.Game.Rulesets.Edit /// public interface IBeatmapVerifier { - public IEnumerable Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap); + public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context); } } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 3e836c4010..96d056fba4 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -40,6 +40,7 @@ namespace osu.Game.Screens.Edit.Verify private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; + private BeatmapVerifierContext context; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) @@ -57,6 +58,8 @@ namespace osu.Game.Screens.Edit.Verify InterpretedDifficulty = new Bindable(beatmap.BeatmapInfo.DifficultyRating); + context = new BeatmapVerifierContext(workingBeatmap.Value); + RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -101,10 +104,10 @@ namespace osu.Game.Screens.Edit.Verify public void Refresh() { - var issues = generalVerifier.Run(beatmap, workingBeatmap.Value); + var issues = generalVerifier.Run(beatmap, context); if (rulesetVerifier != null) - issues = issues.Concat(rulesetVerifier.Run(beatmap, workingBeatmap.Value)); + issues = issues.Concat(rulesetVerifier.Run(beatmap, context)); issues = filter(issues); From 64d96b06a66c9f35026b925573b9f3fc7ebfb72d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 02:30:21 +0200 Subject: [PATCH 081/215] Add interpreted difficulty info to `BeatmapVerifierContext` Enables checks to make use of the difficulty level as shown in the settings UI. --- osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs | 8 +++++++- osu.Game/Screens/Edit/Verify/IssueList.cs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs index 86ca81491a..59d43ba3d6 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs @@ -17,9 +17,15 @@ namespace osu.Game.Rulesets.Edit /// public readonly IWorkingBeatmap WorkingBeatmap; - public BeatmapVerifierContext(IWorkingBeatmap workingBeatmap) + /// + /// The difficulty level which the current beatmap is considered to be. + /// + public readonly Bindable InterpretedDifficulty; + + public BeatmapVerifierContext(IWorkingBeatmap workingBeatmap, DifficultyRating difficultyRating = DifficultyRating.ExpertPlus) { WorkingBeatmap = workingBeatmap; + InterpretedDifficulty = new Bindable(difficultyRating); } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 96d056fba4..2ae86ed198 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -59,6 +59,7 @@ namespace osu.Game.Screens.Edit.Verify InterpretedDifficulty = new Bindable(beatmap.BeatmapInfo.DifficultyRating); context = new BeatmapVerifierContext(workingBeatmap.Value); + context.InterpretedDifficulty.BindTo(InterpretedDifficulty); RelativeSizeAxes = Axes.Both; From b7bc42e0d37db4f5d5bfc201d70e036e25bf9ce7 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 12 May 2021 02:34:16 +0200 Subject: [PATCH 082/215] Rename "playableBeatmap" check arg to "beatmap" The working beatmap is now in the context, so it's easier to distinguish beatmap type, hence no need for this prefix. --- .../Edit/Checks/CheckOffscreenObjects.cs | 4 ++-- osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs | 4 ++-- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 4 ++-- osu.Game/Rulesets/Edit/Checks/CheckAudioPresence.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs | 4 ++-- .../Rulesets/Edit/Checks/CheckBackgroundPresence.cs | 2 +- .../Rulesets/Edit/Checks/CheckBackgroundQuality.cs | 6 +++--- .../Rulesets/Edit/Checks/CheckConcurrentObjects.cs | 10 +++++----- osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs | 8 ++++---- osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs | 6 +++--- osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs | 4 ++-- osu.Game/Rulesets/Edit/IBeatmapVerifier.cs | 2 +- 12 files changed, 28 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c25539201e..86bb7f203f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks new IssueTemplateOffscreenSlider(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - foreach (var hitobject in playableBeatmap.HitObjects) + foreach (var hitobject in beatmap.HitObjects) { switch (hitobject) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 18faeb4f9f..c62f472d75 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -17,9 +17,9 @@ namespace osu.Game.Rulesets.Osu.Edit new CheckOffscreenObjects() }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(playableBeatmap, context)); + return checks.SelectMany(check => check.Run(beatmap, context)); } } } diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 50902d3ff1..1860d54b57 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -29,9 +29,9 @@ namespace osu.Game.Rulesets.Edit new CheckConcurrentObjects() }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(playableBeatmap, context)); + return checks.SelectMany(check => check.Run(beatmap, context)); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioPresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioPresence.cs index 2d572a521e..94c48c300a 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioPresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioPresence.cs @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Edit.Checks { protected override CheckCategory Category => CheckCategory.Audio; protected override string TypeOfFile => "audio"; - protected override string GetFilename(IBeatmap playableBeatmap) => playableBeatmap.Metadata?.AudioFile; + protected override string GetFilename(IBeatmap beatmap) => beatmap.Metadata?.AudioFile; } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs index 82e572f0b3..1015f267aa 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateNoBitrate(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - var audioFile = playableBeatmap.Metadata?.AudioFile; + var audioFile = beatmap.Metadata?.AudioFile; if (audioFile == null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundPresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundPresence.cs index 233c708a25..067800b409 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundPresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundPresence.cs @@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Edit.Checks { protected override CheckCategory Category => CheckCategory.Resources; protected override string TypeOfFile => "background"; - protected override string GetFilename(IBeatmap playableBeatmap) => playableBeatmap.Metadata?.BackgroundFile; + protected override string GetFilename(IBeatmap beatmap) => beatmap.Metadata?.BackgroundFile; } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs index 2af1eca340..87f5c80c89 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs @@ -30,9 +30,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateTooUncompressed(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - var backgroundFile = playableBeatmap.Metadata?.BackgroundFile; + var backgroundFile = beatmap.Metadata?.BackgroundFile; if (backgroundFile == null) yield break; @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Edit.Checks else if (texture.Width < low_width || texture.Height < low_height) yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height); - string storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); + string storagePath = beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); double filesizeMb = context.WorkingBeatmap.GetStream(storagePath).Length / (1024d * 1024d); if (filesizeMb > max_filesize_mb) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs index d6e779d147..fd6ed664e6 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs @@ -22,15 +22,15 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateConcurrentDifferent(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - for (int i = 0; i < playableBeatmap.HitObjects.Count - 1; ++i) + for (int i = 0; i < beatmap.HitObjects.Count - 1; ++i) { - var hitobject = playableBeatmap.HitObjects[i]; + var hitobject = beatmap.HitObjects[i]; - for (int j = i + 1; j < playableBeatmap.HitObjects.Count; ++j) + for (int j = i + 1; j < beatmap.HitObjects.Count; ++j) { - var nextHitobject = playableBeatmap.HitObjects[j]; + var nextHitobject = beatmap.HitObjects[j]; // Accounts for rulesets with hitobjects separated by columns, such as Mania. // In these cases we only care about concurrent objects within the same column. diff --git a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs index cf990a2106..f04909d175 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit.Checks { protected abstract CheckCategory Category { get; } protected abstract string TypeOfFile { get; } - protected abstract string GetFilename(IBeatmap playableBeatmap); + protected abstract string GetFilename(IBeatmap beatmap); public CheckMetadata Metadata => new CheckMetadata(Category, $"Missing {TypeOfFile}"); @@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateDoesNotExist(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - var filename = GetFilename(playableBeatmap); + var filename = GetFilename(beatmap); if (filename == null) { @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit.Checks } // If the file is set, also make sure it still exists. - var storagePath = playableBeatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); + var storagePath = beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); if (storagePath != null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs index f073915a39..aa19f3df07 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs @@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateSmallUnsnap(this) }; - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context) + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) { - var controlPointInfo = playableBeatmap.ControlPointInfo; + var controlPointInfo = beatmap.ControlPointInfo; - foreach (var hitobject in playableBeatmap.HitObjects) + foreach (var hitobject in beatmap.HitObjects) { double startUnsnap = hitobject.StartTime - controlPointInfo.GetClosestSnappedTime(hitobject.StartTime); string startPostfix = hitobject is IHasDuration ? "start" : ""; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs index f669e2161c..511d6aaa0f 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs @@ -24,8 +24,8 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Runs this check and returns any issues detected for the provided beatmap. /// - /// The playable beatmap of the beatmap to run the check on. + /// The playable beatmap of the beatmap to run the check on. /// The beatmap verifier context associated with the beatmap. - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context); + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context); } } diff --git a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs index 4e712351e0..1dafc6938e 100644 --- a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs @@ -12,6 +12,6 @@ namespace osu.Game.Rulesets.Edit /// public interface IBeatmapVerifier { - public IEnumerable Run(IBeatmap playableBeatmap, BeatmapVerifierContext context); + public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context); } } From 1d383024e226f72001902bb3c5587e472aedec9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 13:51:29 +0900 Subject: [PATCH 083/215] Improve the visual appearance of skin editor blueprints --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 67 ++++++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index b8dfdbad0a..23411f2b3d 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -2,14 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Edit; using osuTK; +using osuTK.Graphics; namespace osu.Game.Skinning.Editor { @@ -17,14 +19,19 @@ namespace osu.Game.Skinning.Editor { private Container box; + private Container outlineBox; + private Drawable drawable => (Drawable)Item; /// /// Whether the blueprint should be shown even when the is not alive. /// - protected virtual bool AlwaysShowWhenSelected => false; + protected virtual bool AlwaysShowWhenSelected => true; - protected override bool ShouldBeAlive => (drawable.IsAlive && Item.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); + protected override bool ShouldBeAlive => drawable.IsAlive && Item.IsPresent; + + [Resolved] + private OsuColour colours { get; set; } public SkinBlueprint(ISkinnableComponent component) : base(component) @@ -32,26 +39,68 @@ namespace osu.Game.Skinning.Editor } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { InternalChildren = new Drawable[] { box = new Container { - Colour = colours.Yellow, Children = new Drawable[] { - new Box + outlineBox = new Container { RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - AlwaysPresent = true, + Masking = true, + BorderThickness = 3, + BorderColour = Color4.White, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0f, + AlwaysPresent = true, + }, + } }, - } + new OsuSpriteText + { + Text = Item.GetType().Name, + Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold), + Anchor = Anchor.BottomRight, + Origin = Anchor.TopRight, + } + }, }, }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + updateSelectedState(); + box.FadeInFromZero(200, Easing.OutQuint); + } + + protected override void OnSelected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + updateSelectedState(); + } + + protected override void OnDeselected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + updateSelectedState(); + } + + private void updateSelectedState() + { + outlineBox.FadeColour(colours.Pink.Opacity(IsSelected ? 1 : 0.5f), 200, Easing.OutQuint); + outlineBox.Child.FadeTo(IsSelected ? 0.2f : 0, 200, Easing.OutQuint); + } + private Quad drawableQuad; public override Quad ScreenSpaceDrawQuad => drawableQuad; From 96d4011de2927520121581413837f34e78657abe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 14:02:20 +0900 Subject: [PATCH 084/215] Use pattern matching to tidy up instance construction --- osu.Game/Skinning/Editor/SkinEditor.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 08fc458b94..b32d117a92 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -166,15 +166,13 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { - var instance = (Drawable)Activator.CreateInstance(type) as ISkinnableComponent; - - if (instance == null) + if (!(Activator.CreateInstance(type) is ISkinnableComponent component)) throw new InvalidOperationException("Attempted to instantiate a component for placement which was not an {typeof(ISkinnableComponent)}."); - getTarget(SkinnableTarget.MainHUDComponents)?.Add(instance); + getTarget(SkinnableTarget.MainHUDComponents)?.Add(component); SelectedComponents.Clear(); - SelectedComponents.Add(instance); + SelectedComponents.Add(component); } private ISkinnableTarget getTarget(SkinnableTarget target) From 42e67952517e43eef66f688099bd5490efe6ea36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 14:11:40 +0900 Subject: [PATCH 085/215] Place new skin components at the centre of the screen by default --- osu.Game/Skinning/Editor/SkinEditor.cs | 14 +++++++++++++- osu.Game/Skinning/ISkinnableTarget.cs | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index b32d117a92..4e38caa806 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -166,10 +166,22 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { + var targetContainer = getTarget(SkinnableTarget.MainHUDComponents); + + if (targetContainer == null) + return; + if (!(Activator.CreateInstance(type) is ISkinnableComponent component)) throw new InvalidOperationException("Attempted to instantiate a component for placement which was not an {typeof(ISkinnableComponent)}."); - getTarget(SkinnableTarget.MainHUDComponents)?.Add(component); + var drawableComponent = (Drawable)component; + + // give newly added components a sane starting location. + drawableComponent.Origin = Anchor.TopCentre; + drawableComponent.Anchor = Anchor.TopCentre; + drawableComponent.Y = targetContainer.DrawSize.Y / 2; + + targetContainer.Add(component); SelectedComponents.Clear(); SelectedComponents.Add(component); diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 4d97b42e8e..4f6e3e66c3 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -1,12 +1,14 @@ // 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.Graphics; + namespace osu.Game.Skinning { /// /// Denotes a container which can house s. /// - public interface ISkinnableTarget + public interface ISkinnableTarget : IDrawable { public SkinnableTarget Target { get; } From d55f42dc2eed23c66208bd906386c09ff0a8dcb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 15:05:53 +0900 Subject: [PATCH 086/215] Show anchor and origin in skin blueprints when selected --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 64 ++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 23411f2b3d..7db5bc7ead 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.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 osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -21,6 +22,8 @@ namespace osu.Game.Skinning.Editor private Container outlineBox; + private AnchorOriginVisualiser anchorOriginVisualiser; + private Drawable drawable => (Drawable)Item; /// @@ -69,9 +72,13 @@ namespace osu.Game.Skinning.Editor Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold), Anchor = Anchor.BottomRight, Origin = Anchor.TopRight, - } + }, }, }, + anchorOriginVisualiser = new AnchorOriginVisualiser(drawable) + { + Alpha = 0, + } }; } @@ -80,7 +87,7 @@ namespace osu.Game.Skinning.Editor base.LoadComplete(); updateSelectedState(); - box.FadeInFromZero(200, Easing.OutQuint); + this.FadeInFromZero(200, Easing.OutQuint); } protected override void OnSelected() @@ -99,6 +106,8 @@ namespace osu.Game.Skinning.Editor { outlineBox.FadeColour(colours.Pink.Opacity(IsSelected ? 1 : 0.5f), 200, Easing.OutQuint); outlineBox.Child.FadeTo(IsSelected ? 0.2f : 0, 200, Easing.OutQuint); + + anchorOriginVisualiser.FadeTo(IsSelected ? 1 : 0, 200, Easing.OutQuint); } private Quad drawableQuad; @@ -123,4 +132,55 @@ namespace osu.Game.Skinning.Editor public override Quad SelectionQuad => drawable.ScreenSpaceDrawQuad; } + + internal class AnchorOriginVisualiser : CompositeDrawable + { + private readonly Drawable drawable; + + private readonly Box originBox; + + private readonly Box anchorBox; + private readonly Box anchorLine; + + public AnchorOriginVisualiser(Drawable drawable) + { + this.drawable = drawable; + + InternalChildren = new Drawable[] + { + anchorLine = new Box + { + Colour = Color4.Yellow, + Height = 2, + }, + originBox = new Box + { + Colour = Color4.Red, + Origin = Anchor.Centre, + Size = new Vector2(5), + }, + anchorBox = new Box + { + Colour = Color4.Red, + Origin = Anchor.Centre, + Size = new Vector2(5), + }, + }; + } + + protected override void Update() + { + base.Update(); + + originBox.Position = drawable.ToSpaceOfOtherDrawable(drawable.OriginPosition, this); + anchorBox.Position = drawable.Parent.ToSpaceOfOtherDrawable(drawable.AnchorPosition, this); + + var point1 = ToLocalSpace(anchorBox.ScreenSpaceDrawQuad.Centre); + var point2 = ToLocalSpace(originBox.ScreenSpaceDrawQuad.Centre); + + anchorLine.Position = point1; + anchorLine.Width = (point2 - point1).Length; + anchorLine.Rotation = MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X)); + } + } } From 05e0c57a6af3fd579abc9ea73fd4de33702f9284 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 15:30:52 +0900 Subject: [PATCH 087/215] Keep component positions stable when changing anchor/origin --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index cf5ece03e9..410ec7a272 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -109,13 +109,25 @@ namespace osu.Game.Skinning.Editor private void applyOrigin(Anchor anchor) { foreach (var item in SelectedItems) - ((Drawable)item).Origin = anchor; + { + var drawable = (Drawable)item; + + var previousOrigin = drawable.OriginPosition; + drawable.Origin = anchor; + drawable.Position += drawable.OriginPosition - previousOrigin; + } } private void applyAnchor(Anchor anchor) { foreach (var item in SelectedItems) - ((Drawable)item).Anchor = anchor; + { + var drawable = (Drawable)item; + + var previousAnchor = (drawable.AnchorPosition); + drawable.Anchor = anchor; + drawable.Position -= drawable.AnchorPosition - previousAnchor; + } } private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference) From 29e6f6b6b6cc1cc8931c13525da01f224ae8de89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 15:58:21 +0900 Subject: [PATCH 088/215] Remove `public` prefixes from interface type and add `Components` list for future use --- .../Skinning/Editor/SkinBlueprintContainer.cs | 3 +-- osu.Game/Skinning/ISkinnableTarget.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index ef189ed165..9890b8e8b6 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -12,7 +12,6 @@ using osu.Framework.Testing; using osu.Game.Rulesets.Edit; using osu.Game.Screens; using osu.Game.Screens.Edit.Compose.Components; -using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning.Editor { @@ -38,7 +37,7 @@ namespace osu.Game.Skinning.Editor base.LoadComplete(); // track each target container on the current screen. - var targetContainers = target.ChildrenOfType().ToArray(); + var targetContainers = target.ChildrenOfType().ToArray(); if (targetContainers.Length == 0) { diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 4d97b42e8e..ab3c24a1e2 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -1,6 +1,8 @@ // 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.Bindables; + namespace osu.Game.Skinning { /// @@ -8,16 +10,24 @@ namespace osu.Game.Skinning /// public interface ISkinnableTarget { - public SkinnableTarget Target { get; } + /// + /// The definition of this target. + /// + SkinnableTarget Target { get; } + + /// + /// A bindable list of components which are being tracked by this skinnable target. + /// + IBindableList Components { get; } /// /// Reload this target from the current skin. /// - public void Reload(); + void Reload(); /// /// Add the provided item to this target. /// - public void Add(ISkinnableComponent drawable); + void Add(ISkinnableComponent drawable); } } From 494a1b01a536d6470b2700d4879cd0a05dc13e7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 15:59:33 +0900 Subject: [PATCH 089/215] Move `SkinnableElementTargetContainer` out of HUD namespace --- osu.Game/Skinning/Editor/SkinEditor.cs | 1 - .../Play/HUD => Skinning}/SkinnableElementTargetContainer.cs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game/{Screens/Play/HUD => Skinning}/SkinnableElementTargetContainer.cs (97%) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index b32d117a92..a00557ee4e 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play.HUD; using osuTK; namespace osu.Game.Skinning.Editor diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableElementTargetContainer.cs similarity index 97% rename from osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs rename to osu.Game/Skinning/SkinnableElementTargetContainer.cs index b2f6d32927..b900fdf3e0 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableElementTargetContainer.cs @@ -7,9 +7,9 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Extensions; -using osu.Game.Skinning; +using osu.Game.Screens.Play.HUD; -namespace osu.Game.Screens.Play.HUD +namespace osu.Game.Skinning { public class SkinnableElementTargetContainer : SkinReloadableDrawable, ISkinnableTarget { From 9df08560b69519f94779e22e2ce6c49433b1808b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 16:07:00 +0900 Subject: [PATCH 090/215] Save skin editor changes on forced exit --- osu.Game/Skinning/Editor/SkinEditor.cs | 4 ++-- osu.Game/Skinning/Editor/SkinEditorOverlay.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index a00557ee4e..5deebe9800 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -108,7 +108,7 @@ namespace osu.Game.Skinning.Editor { Text = "Save Changes", Width = 140, - Action = save, + Action = Save, }, new DangerousTriangleButton { @@ -192,7 +192,7 @@ namespace osu.Game.Skinning.Editor } } - private void save() + public void Save() { SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); diff --git a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs index cbed498a38..2d7cae71ff 100644 --- a/osu.Game/Skinning/Editor/SkinEditorOverlay.cs +++ b/osu.Game/Skinning/Editor/SkinEditorOverlay.cs @@ -92,8 +92,10 @@ namespace osu.Game.Skinning.Editor /// public void Reset() { + skinEditor?.Save(); skinEditor?.Hide(); skinEditor?.Expire(); + skinEditor = null; } } From 17e376457610652e9cfac1470eb8748114441022 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 16:38:04 +0900 Subject: [PATCH 091/215] Rename `Settings` to have a more localised name --- .../Edit/{Settings.cs => RoundedContentEditorScreenSettings.cs} | 2 +- osu.Game/Screens/Edit/Timing/ControlPointSettings.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename osu.Game/Screens/Edit/{Settings.cs => RoundedContentEditorScreenSettings.cs} (94%) diff --git a/osu.Game/Screens/Edit/Settings.cs b/osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs similarity index 94% rename from osu.Game/Screens/Edit/Settings.cs rename to osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs index 758414333d..06293fbaac 100644 --- a/osu.Game/Screens/Edit/Settings.cs +++ b/osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays; namespace osu.Game.Screens.Edit { - public abstract class Settings : CompositeDrawable + public abstract class RoundedContentEditorScreenSettings : CompositeDrawable { [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) diff --git a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs index 36f31d4be4..13f81c75da 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.Edit.Timing { - public class ControlPointSettings : Settings + public class ControlPointSettings : RoundedContentEditorScreenSettings { protected override IReadOnlyList CreateSections() => new Drawable[] { diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 68ab51c3c8..506694aa46 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.Edit.Verify { - public class IssueSettings : Settings + public class IssueSettings : RoundedContentEditorScreenSettings { private readonly IssueList issueList; From d2e0e8ad948aac6ef137abf81d50963ba8e55297 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 16:53:49 +0900 Subject: [PATCH 092/215] Reverse direction of binding to allow for better abstract class definitions --- ...tEditorScreen.cs => EditorRoundedScreen.cs} | 4 ++-- ...tings.cs => EditorRoundedScreenSettings.cs} | 2 +- ...s => EditorRoundedScreenSettingsSection.cs} | 11 ++--------- osu.Game/Screens/Edit/Setup/SetupScreen.cs | 2 +- .../Screens/Edit/Setup/SetupScreenHeader.cs | 2 +- osu.Game/Screens/Edit/Setup/SetupSection.cs | 2 +- .../Edit/Timing/ControlPointSettings.cs | 2 +- osu.Game/Screens/Edit/Timing/TimingScreen.cs | 2 +- .../Edit/Verify/InterpretationSection.cs | 11 +++++------ osu.Game/Screens/Edit/Verify/IssueList.cs | 5 +++-- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 13 +++---------- osu.Game/Screens/Edit/Verify/IssueTable.cs | 5 ++++- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 18 +++++++++++------- .../Screens/Edit/Verify/VisibilitySection.cs | 14 ++++++-------- 14 files changed, 42 insertions(+), 51 deletions(-) rename osu.Game/Screens/Edit/{RoundedContentEditorScreen.cs => EditorRoundedScreen.cs} (93%) rename osu.Game/Screens/Edit/{RoundedContentEditorScreenSettings.cs => EditorRoundedScreenSettings.cs} (94%) rename osu.Game/Screens/Edit/{Verify/Section.cs => EditorRoundedScreenSettingsSection.cs} (88%) diff --git a/osu.Game/Screens/Edit/RoundedContentEditorScreen.cs b/osu.Game/Screens/Edit/EditorRoundedScreen.cs similarity index 93% rename from osu.Game/Screens/Edit/RoundedContentEditorScreen.cs rename to osu.Game/Screens/Edit/EditorRoundedScreen.cs index a55ae3f635..c6ced02021 100644 --- a/osu.Game/Screens/Edit/RoundedContentEditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreen.cs @@ -10,7 +10,7 @@ using osu.Game.Overlays; namespace osu.Game.Screens.Edit { - public class RoundedContentEditorScreen : EditorScreen + public class EditorRoundedScreen : EditorScreen { public const int HORIZONTAL_PADDING = 100; @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit protected override Container Content => roundedContent; - public RoundedContentEditorScreen(EditorScreenMode mode) + public EditorRoundedScreen(EditorScreenMode mode) : base(mode) { ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); diff --git a/osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs b/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs similarity index 94% rename from osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs rename to osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs index 06293fbaac..cb17484d27 100644 --- a/osu.Game/Screens/Edit/RoundedContentEditorScreenSettings.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreenSettings.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays; namespace osu.Game.Screens.Edit { - public abstract class RoundedContentEditorScreenSettings : CompositeDrawable + public abstract class EditorRoundedScreenSettings : CompositeDrawable { [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) diff --git a/osu.Game/Screens/Edit/Verify/Section.cs b/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs similarity index 88% rename from osu.Game/Screens/Edit/Verify/Section.cs rename to osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs index f6815a05a1..87ed98a439 100644 --- a/osu.Game/Screens/Edit/Verify/Section.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs @@ -9,22 +9,15 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osuTK; -namespace osu.Game.Screens.Edit.Verify +namespace osu.Game.Screens.Edit { - public abstract class Section : CompositeDrawable + public abstract class EditorRoundedScreenSettingsSection : CompositeDrawable { private const int header_height = 50; - protected readonly IssueList IssueList; - protected FillFlowContainer Flow; protected abstract string Header { get; } - protected Section(IssueList issueList) - { - IssueList = issueList; - } - [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 0af7cf095b..5bbec2574f 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Edit.Setup { - public class SetupScreen : RoundedContentEditorScreen + public class SetupScreen : EditorRoundedScreen { [Cached] private SectionsContainer sections = new SectionsContainer(); diff --git a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs index 10daacc359..2d0afda001 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Edit.Setup public SetupScreenTabControl() { - TabContainer.Margin = new MarginPadding { Horizontal = RoundedContentEditorScreen.HORIZONTAL_PADDING }; + TabContainer.Margin = new MarginPadding { Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING }; AddInternal(background = new Box { diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index b3ae15900f..8964e651df 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Edit.Setup Padding = new MarginPadding { Vertical = 10, - Horizontal = RoundedContentEditorScreen.HORIZONTAL_PADDING + Horizontal = EditorRoundedScreen.HORIZONTAL_PADDING }; InternalChild = new FillFlowContainer diff --git a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs index 13f81c75da..48639789af 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.Edit.Timing { - public class ControlPointSettings : RoundedContentEditorScreenSettings + public class ControlPointSettings : EditorRoundedScreenSettings { protected override IReadOnlyList CreateSections() => new Drawable[] { diff --git a/osu.Game/Screens/Edit/Timing/TimingScreen.cs b/osu.Game/Screens/Edit/Timing/TimingScreen.cs index 9f26dece08..a4193d5084 100644 --- a/osu.Game/Screens/Edit/Timing/TimingScreen.cs +++ b/osu.Game/Screens/Edit/Timing/TimingScreen.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Timing { - public class TimingScreen : RoundedContentEditorScreen + public class TimingScreen : EditorRoundedScreen { [Cached] private Bindable selectedGroup = new Bindable(); diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs index fa0bde9ddc..7991786542 100644 --- a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -8,12 +8,10 @@ using osu.Game.Overlays.Settings; namespace osu.Game.Screens.Edit.Verify { - internal class InterpretationSection : Section + internal class InterpretationSection : EditorRoundedScreenSettingsSection { - public InterpretationSection(IssueList issueList) - : base(issueList) - { - } + [Resolved] + private VerifyScreen verify { get; set; } protected override string Header => "Interpretation"; @@ -26,7 +24,8 @@ namespace osu.Game.Screens.Edit.Verify Origin = Anchor.CentreLeft, TooltipText = "Affects checks that depend on difficulty level" }; - dropdown.Current.BindTo(IssueList.InterpretedDifficulty); + + dropdown.Current.BindTo(verify.InterpretedDifficulty); Flow.Add(dropdown); } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 3e836c4010..befffdd9db 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -18,6 +18,7 @@ using osuTK; namespace osu.Game.Screens.Edit.Verify { + [Cached] public class IssueList : CompositeDrawable { private IssueTable table; @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Edit.Verify private EditorBeatmap beatmap { get; set; } [Resolved] - private Bindable selectedIssue { get; set; } + private VerifyScreen verify { get; set; } public Dictionary> ShowType { get; set; } @@ -55,7 +56,7 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); - InterpretedDifficulty = new Bindable(beatmap.BeatmapInfo.DifficultyRating); + InterpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 506694aa46..ae3ef7e0b0 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -6,19 +6,12 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.Edit.Verify { - public class IssueSettings : RoundedContentEditorScreenSettings + public class IssueSettings : EditorRoundedScreenSettings { - private readonly IssueList issueList; - - public IssueSettings(IssueList issueList) - { - this.issueList = issueList; - } - protected override IReadOnlyList CreateSections() => new Drawable[] { - new InterpretationSection(issueList), - new VisibilitySection(issueList) + new InterpretationSection(), + new VisibilitySection() }; } } diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 44244028c9..05a8fdd26d 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -18,7 +18,9 @@ namespace osu.Game.Screens.Edit.Verify public class IssueTable : EditorTable { [Resolved] - private Bindable selectedIssue { get; set; } + private VerifyScreen verify { get; set; } + + private Bindable selectedIssue; [Resolved] private EditorClock clock { get; set; } @@ -71,6 +73,7 @@ namespace osu.Game.Screens.Edit.Verify { base.LoadComplete(); + selectedIssue = verify.SelectedIssue.GetBoundCopy(); selectedIssue.BindValueChanged(issue => { foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index ed4658da8e..afd0c8760d 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -5,14 +5,19 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Screens.Edit.Verify { - public class VerifyScreen : RoundedContentEditorScreen + [Cached] + public class VerifyScreen : EditorRoundedScreen { - [Cached] - private Bindable selectedIssue = new Bindable(); + public readonly Bindable SelectedIssue = new Bindable(); + + public readonly Bindable InterpretedDifficulty = new Bindable(); + + public IssueList IssueList { get; private set; } public VerifyScreen() : base(EditorScreenMode.Verify) @@ -22,8 +27,7 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load() { - IssueList issueList; - + IssueList = new IssueList(); Child = new Container { RelativeSizeAxes = Axes.Both, @@ -39,8 +43,8 @@ namespace osu.Game.Screens.Edit.Verify { new Drawable[] { - issueList = new IssueList(), - new IssueSettings(issueList), + IssueList, + new IssueSettings(), }, } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index f849426800..ce755bdcb4 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -9,19 +9,17 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Screens.Edit.Verify { - internal class VisibilitySection : Section + internal class VisibilitySection : EditorRoundedScreenSettingsSection { - public VisibilitySection(IssueList issueList) - : base(issueList) - { - } + [Resolved] + private VerifyScreen verify { get; set; } protected override string Header => "Visibility"; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - foreach (IssueType issueType in IssueList.ShowType.Keys) + foreach (IssueType issueType in verify.IssueList.ShowType.Keys) { var checkbox = new SettingsCheckbox { @@ -30,8 +28,8 @@ namespace osu.Game.Screens.Edit.Verify LabelText = issueType.ToString() }; - checkbox.Current.BindTo(IssueList.ShowType[issueType]); - checkbox.Current.BindValueChanged(_ => IssueList.Refresh()); + checkbox.Current.BindTo(verify.IssueList.ShowType[issueType]); + checkbox.Current.BindValueChanged(_ => verify.IssueList.Refresh()); Flow.Add(checkbox); } } From be187e8ebdd8d981b1c3c1620960e1aaca27d44b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 17:42:04 +0900 Subject: [PATCH 093/215] Avoid hard crash if `Save()` is called before preparing for mutation --- osu.Game/Skinning/Editor/SkinEditor.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 5deebe9800..fcb1392ed5 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -38,6 +38,8 @@ namespace osu.Game.Skinning.Editor [Resolved] private OsuColour colours { get; set; } + private bool hasBegunMutating; + public SkinEditor(Drawable targetScreen) { this.targetScreen = targetScreen; @@ -139,7 +141,11 @@ namespace osu.Game.Skinning.Editor // schedule ensures this only happens when the skin editor is visible. // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). // probably something which will be factored out in a future database refactor so not too concerning for now. - currentSkin.BindValueChanged(skin => Scheduler.AddOnce(skinChanged), true); + currentSkin.BindValueChanged(skin => + { + hasBegunMutating = false; + Scheduler.AddOnce(skinChanged); + }, true); } private void skinChanged() @@ -161,6 +167,7 @@ namespace osu.Game.Skinning.Editor }); skins.EnsureMutableSkin(); + hasBegunMutating = true; } private void placeComponent(Type type) @@ -194,6 +201,9 @@ namespace osu.Game.Skinning.Editor public void Save() { + if (!hasBegunMutating) + return; + SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) From 2f55d1e5ab9c734bb07519849d6ca4c31f32b375 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 17:42:12 +0900 Subject: [PATCH 094/215] Also save on skin switch --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index fcb1392ed5..9e50aab829 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -143,6 +143,8 @@ namespace osu.Game.Skinning.Editor // probably something which will be factored out in a future database refactor so not too concerning for now. currentSkin.BindValueChanged(skin => { + Save(); + hasBegunMutating = false; Scheduler.AddOnce(skinChanged); }, true); From 80e231d90ad9a25444b7a2e2c1d43413cbaf327e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 May 2021 11:07:31 +0300 Subject: [PATCH 095/215] Add failing test case --- .../Visual/Editing/TestSceneComposeSelectBox.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index e383aa8008..d5cfeb1878 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -167,5 +167,21 @@ namespace osu.Game.Tests.Visual.Editing AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20))); AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0); } + + /// + /// Tests that hovering over two handles instantaneously from one to another does not crash or cause issues to the visibility state. + /// + [Test] + public void TestHoverOverTwoHandlesInstantaneously() + { + AddStep("hover over top-left scale handle", () => + InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == Anchor.TopLeft))); + AddStep("hover over top-right scale handle", () => + InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == Anchor.TopRight))); + AddUntilStep("top-left rotation handle hidden", () => + this.ChildrenOfType().Single(r => r.Anchor == Anchor.TopLeft).Alpha == 0); + AddUntilStep("top-right rotation handle shown", () => + this.ChildrenOfType().Single(r => r.Anchor == Anchor.TopRight).Alpha == 1); + } } } From 96d3586294dbfef0f9af5b7a9e70e00a868d0600 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 12 May 2021 11:30:12 +0300 Subject: [PATCH 096/215] Fix rotation handle visibility logic not handling two handles hovered at once --- .../Compose/Components/SelectionBoxDragHandleContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs index 456f72878d..397158b9f6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs @@ -84,8 +84,8 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeHandle?.IsHeld == true) return; - activeHandle = rotationHandles.SingleOrDefault(h => h.IsHeld || h.IsHovered); - activeHandle ??= allDragHandles.SingleOrDefault(h => h.IsHovered); + activeHandle = rotationHandles.FirstOrDefault(h => h.IsHeld || h.IsHovered); + activeHandle ??= allDragHandles.FirstOrDefault(h => h.IsHovered); if (activeHandle != null) { From 088335a0359f73ecb10625c5bcccbaefff9edbd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 17:45:51 +0900 Subject: [PATCH 097/215] Revert "Also save on skin switch" This reverts commit 2f55d1e5ab9c734bb07519849d6ca4c31f32b375. --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 9e50aab829..fcb1392ed5 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -143,8 +143,6 @@ namespace osu.Game.Skinning.Editor // probably something which will be factored out in a future database refactor so not too concerning for now. currentSkin.BindValueChanged(skin => { - Save(); - hasBegunMutating = false; Scheduler.AddOnce(skinChanged); }, true); From 0a895fff158ab57c8dc0e9a4ff9e6d9f5a7b218e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 18:53:25 +0900 Subject: [PATCH 098/215] Remove remaining test usage of `SkinnableXXX` HUD components --- .../Visual/Gameplay/TestSceneComboCounter.cs | 8 ++------ .../TestSceneSkinnableAccuracyCounter.cs | 3 ++- .../TestSceneSkinnableHealthDisplay.cs | 11 ++-------- .../TestSceneSkinnableScoreCounter.cs | 11 +++------- .../Play/HUD/SkinnableAccuracyCounter.cs | 16 --------------- .../Screens/Play/HUD/SkinnableComboCounter.cs | 16 --------------- .../Play/HUD/SkinnableHealthDisplay.cs | 16 --------------- .../Screens/Play/HUD/SkinnableScoreCounter.cs | 16 --------------- osu.Game/Skinning/DefaultSkin.cs | 20 +++++++++++++++++++ osu.Game/Skinning/SkinnableDrawable.cs | 4 ++-- 10 files changed, 31 insertions(+), 90 deletions(-) delete mode 100644 osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs delete mode 100644 osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs delete mode 100644 osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs delete mode 100644 osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs index b0a0b5189f..b22af0f7ac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneComboCounter.cs @@ -1,22 +1,18 @@ // 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.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneComboCounter : SkinnableTestScene { - private IEnumerable comboCounters => CreatedDrawables.OfType(); - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); [Cached] @@ -25,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("Create combo counters", () => SetContents(() => new SkinnableComboCounter())); + AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter)))); } [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs index 6a8a2187f9..d9139299ea 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { @@ -22,7 +23,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void SetUpSteps() { AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1); - AddStep("Create accuracy counters", () => SetContents(() => new SkinnableAccuracyCounter())); + AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)))); } [Test] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 4f50613416..ead27bf017 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -1,8 +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.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing; @@ -12,14 +10,12 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneSkinnableHealthDisplay : SkinnableTestScene { - private IEnumerable healthDisplays => CreatedDrawables.OfType(); - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); [Cached(typeof(HealthProcessor))] @@ -28,10 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("Create health displays", () => - { - SetContents(() => new SkinnableHealthDisplay()); - }); + AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)))); AddStep(@"Reset all", delegate { healthProcessor.Health.Value = 1; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs index 4f2183711e..8d633c3ca2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableScoreCounter.cs @@ -1,23 +1,18 @@ // 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.Extensions.IEnumerableExtensions; using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay { public class TestSceneSkinnableScoreCounter : SkinnableTestScene { - private IEnumerable scoreCounters => CreatedDrawables.OfType(); - protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); [Cached] @@ -26,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("Create score counters", () => SetContents(() => new SkinnableScoreCounter())); + AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)))); } [Test] @@ -40,7 +35,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestVeryLargeScore() { - AddStep("set large score", () => scoreCounters.ForEach(counter => scoreProcessor.TotalScore.Value = 1_000_000_000)); + AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000); } } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs deleted file mode 100644 index fcb8fca35d..0000000000 --- a/osu.Game/Screens/Play/HUD/SkinnableAccuracyCounter.cs +++ /dev/null @@ -1,16 +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.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public class SkinnableAccuracyCounter : SkinnableDrawable - { - public SkinnableAccuracyCounter() - : base(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter), _ => new DefaultAccuracyCounter()) - { - CentreComponent = false; - } - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs deleted file mode 100644 index c62f1460c9..0000000000 --- a/osu.Game/Screens/Play/HUD/SkinnableComboCounter.cs +++ /dev/null @@ -1,16 +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.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public class SkinnableComboCounter : SkinnableDrawable - { - public SkinnableComboCounter() - : base(new HUDSkinComponent(HUDSkinComponents.ComboCounter), skinComponent => new DefaultComboCounter()) - { - CentreComponent = false; - } - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs deleted file mode 100644 index 3ba6a33276..0000000000 --- a/osu.Game/Screens/Play/HUD/SkinnableHealthDisplay.cs +++ /dev/null @@ -1,16 +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.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public class SkinnableHealthDisplay : SkinnableDrawable - { - public SkinnableHealthDisplay() - : base(new HUDSkinComponent(HUDSkinComponents.HealthDisplay), _ => new DefaultHealthDisplay()) - { - CentreComponent = false; - } - } -} diff --git a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs b/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs deleted file mode 100644 index cc9a712e97..0000000000 --- a/osu.Game/Screens/Play/HUD/SkinnableScoreCounter.cs +++ /dev/null @@ -1,16 +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.Game.Skinning; - -namespace osu.Game.Screens.Play.HUD -{ - public class SkinnableScoreCounter : SkinnableDrawable - { - public SkinnableScoreCounter() - : base(new HUDSkinComponent(HUDSkinComponents.ScoreCounter), _ => new DefaultScoreCounter()) - { - CentreComponent = false; - } - } -} diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 3de3dc7702..a9ab4e53c8 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -91,6 +91,26 @@ namespace osu.Game.Skinning } return null; + + case HUDSkinComponent hudComponent: + { + switch (hudComponent.Component) + { + case HUDSkinComponents.ComboCounter: + return new DefaultComboCounter(); + + case HUDSkinComponents.ScoreCounter: + return new DefaultScoreCounter(); + + case HUDSkinComponents.AccuracyCounter: + return new DefaultAccuracyCounter(); + + case HUDSkinComponents.HealthDisplay: + return new DefaultHealthDisplay(); + } + + return null; + } } return base.GetDrawableComponent(component); diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 5a48bc4baf..77f7cf5c3f 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -42,7 +42,7 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(ISkinComponent component, Func defaultImplementation, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) : this(component, allowFallback, confineMode) { createDefault = defaultImplementation; @@ -68,7 +68,7 @@ namespace osu.Game.Skinning private bool isDefault; - protected virtual Drawable CreateDefault(ISkinComponent component) => createDefault(component); + protected virtual Drawable CreateDefault(ISkinComponent component) => createDefault?.Invoke(component) ?? Empty(); /// /// Whether to apply size restrictions (specified via ) to the default implementation. From 75227e5a70f21cb941e249c6e5d80b15b62e956a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 18:55:48 +0900 Subject: [PATCH 099/215] Change default skin to use component lookup for conformity --- osu.Game/Skinning/DefaultSkin.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index a9ab4e53c8..2715c9fce2 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -80,10 +80,10 @@ namespace osu.Game.Skinning { Children = new Drawable[] { - new DefaultComboCounter(), - new DefaultScoreCounter(), - new DefaultAccuracyCounter(), - new DefaultHealthDisplay(), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter)), + GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.HealthDisplay)), } }; From 55e1f97f5947dc8e1d65c14935b4a51eaae7c4ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 19:06:28 +0900 Subject: [PATCH 100/215] Remove unused using statement --- .../Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs | 1 - osu.Game/Skinning/DefaultSkin.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs index d9139299ea..6f4e6a2420 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableAccuracyCounter.cs @@ -7,7 +7,6 @@ using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; namespace osu.Game.Tests.Visual.Gameplay diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 2715c9fce2..3dcfbcda13 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -78,7 +78,7 @@ namespace osu.Game.Skinning } }) { - Children = new Drawable[] + Children = new[] { GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ComboCounter)), GetDrawableComponent(new HUDSkinComponent(HUDSkinComponents.ScoreCounter)), From 7bac81f39487c446d515e5454f49b402680e38ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 12 May 2021 19:37:00 +0900 Subject: [PATCH 101/215] Fix incorrect inline comments Co-authored-by: Salman Ahmed --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 23411f2b3d..58fa255508 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -85,13 +85,13 @@ namespace osu.Game.Skinning.Editor protected override void OnSelected() { - // base logic hides selected blueprints when not selected, but timeline doesn't do that. + // base logic hides selected blueprints when not selected, but skin blueprints don't do that. updateSelectedState(); } protected override void OnDeselected() { - // base logic hides selected blueprints when not selected, but timeline doesn't do that. + // base logic hides selected blueprints when not selected, but skin blueprints don't do that. updateSelectedState(); } From 4464204e336688bcfce0266c2014ec20a7978e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 12 May 2021 22:18:15 +0200 Subject: [PATCH 102/215] Mark all skin ctors used via reflection in `SkinInfo.CreateInstance()` --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ osu.Game/Skinning/DefaultSkin.cs | 2 ++ osu.Game/Skinning/LegacySkin.cs | 1 + 3 files changed, 5 insertions(+) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 564be8630e..7adf53e5e7 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.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 JetBrains.Annotations; using osu.Framework.IO.Stores; using osu.Game.IO; using osuTK.Graphics; @@ -9,6 +10,7 @@ namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) : this(Info, storage, resources) { diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index fcd874d6ca..745bdf0bb2 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using JetBrains.Annotations; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -20,6 +21,7 @@ namespace osu.Game.Skinning { } + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources) : base(skin) { diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index eae3b69233..74250f9684 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -52,6 +52,7 @@ namespace osu.Game.Skinning private readonly Dictionary maniaConfigurations = new Dictionary(); + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public LegacySkin(SkinInfo skin, IStorageResourceProvider resources) : this(skin, new LegacySkinResourceStore(skin, resources.Files), resources, "skin.ini") { From 1b579dd83801849c33ce06a685eab48230cdd648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 12 May 2021 22:42:26 +0200 Subject: [PATCH 103/215] Extract invariant instantiation info extension method --- osu.Game/Extensions/TypeExtensions.cs | 31 +++++++++++++++++++++++++++ osu.Game/Rulesets/Ruleset.cs | 3 ++- osu.Game/Rulesets/RulesetInfo.cs | 16 +------------- osu.Game/Skinning/SkinInfo.cs | 16 +------------- osu.Game/Skinning/SkinManager.cs | 3 ++- 5 files changed, 37 insertions(+), 32 deletions(-) create mode 100644 osu.Game/Extensions/TypeExtensions.cs diff --git a/osu.Game/Extensions/TypeExtensions.cs b/osu.Game/Extensions/TypeExtensions.cs new file mode 100644 index 0000000000..2e93c81758 --- /dev/null +++ b/osu.Game/Extensions/TypeExtensions.cs @@ -0,0 +1,31 @@ +// 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; + +namespace osu.Game.Extensions +{ + internal static class TypeExtensions + { + /// + /// Returns 's + /// with the assembly version, culture and public key token values removed. + /// + /// + /// This method is usually used in extensibility scenarios (i.e. for custom rulesets or skins) + /// when a version-agnostic identifier associated with a C# class - potentially originating from + /// an external assembly - is needed. + /// Leaving only the type and assembly names in such a scenario allows to preserve compatibility + /// across assembly versions. + /// + internal static string GetInvariantInstantiationInfo(this Type type) + { + string assemblyQualifiedName = type.AssemblyQualifiedName; + if (assemblyQualifiedName == null) + throw new ArgumentException($"{type}'s assembly-qualified name is null. Ensure that it is a concrete type and not a generic type parameter.", nameof(type)); + + return string.Join(',', assemblyQualifiedName.Split(',').Take(2)); + } + } +} diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 7f0c27adfc..7bdf84ace4 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -26,6 +26,7 @@ using JetBrains.Annotations; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; +using osu.Game.Extensions; using osu.Game.Rulesets.Filter; using osu.Game.Screens.Ranking.Statistics; @@ -135,7 +136,7 @@ namespace osu.Game.Rulesets Name = Description, ShortName = ShortName, ID = (this as ILegacyRuleset)?.LegacyID, - InstantiationInfo = GetType().AssemblyQualifiedName, + InstantiationInfo = GetType().GetInvariantInstantiationInfo(), Available = true, }; } diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index d5aca8c650..702bf35fa8 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Newtonsoft.Json; using osu.Framework.Testing; @@ -18,20 +17,7 @@ namespace osu.Game.Rulesets public string ShortName { get; set; } - private string instantiationInfo; - - public string InstantiationInfo - { - get => instantiationInfo; - set => instantiationInfo = abbreviateInstantiationInfo(value); - } - - private string abbreviateInstantiationInfo(string value) - { - // exclude version onwards, matching only on namespace and type. - // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. - return string.Join(',', value.Split(',').Take(2)); - } + public string InstantiationInfo { get; set; } [JsonIgnore] public bool Available { get; set; } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 2e29808cb4..a61a6dd1ce 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; @@ -25,20 +24,7 @@ namespace osu.Game.Skinning public string Creator { get; set; } - private string instantiationInfo; - - public string InstantiationInfo - { - get => instantiationInfo; - set => instantiationInfo = abbreviateInstantiationInfo(value); - } - - private string abbreviateInstantiationInfo(string value) - { - // exclude version onwards, matching only on namespace and type. - // this is mainly to allow for new versions of already loaded rulesets to "upgrade" from old. - return string.Join(',', value.Split(',').Take(2)); - } + public string InstantiationInfo { get; set; } public virtual Skin CreateInstance(IResourceStore legacyDefaultResources, IStorageResourceProvider resources) { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index dbb9dcb7fc..b4051286aa 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -22,6 +22,7 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.IO; using osu.Game.IO.Archives; @@ -124,7 +125,7 @@ namespace osu.Game.Skinning var instance = GetSkin(model); - model.InstantiationInfo ??= instance.GetType().AssemblyQualifiedName; + model.InstantiationInfo ??= instance.GetType().GetInvariantInstantiationInfo(); if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true) populateMetadata(model, instance); From a6aec6e0074db91968c3526c4f9c556fba1c2329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 12 May 2021 23:34:25 +0200 Subject: [PATCH 104/215] Fix missed `InstantiationInfo` setter usages --- osu.Game/Skinning/DefaultLegacySkin.cs | 3 ++- osu.Game/Skinning/SkinInfo.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 7adf53e5e7..b05c309e4e 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using osu.Framework.IO.Stores; +using osu.Game.Extensions; using osu.Game.IO; using osuTK.Graphics; @@ -35,7 +36,7 @@ namespace osu.Game.Skinning ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. Name = "osu!classic", Creator = "team osu!", - InstantiationInfo = typeof(DefaultLegacySkin).AssemblyQualifiedName, + InstantiationInfo = typeof(DefaultLegacySkin).GetInvariantInstantiationInfo() }; } } diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index a61a6dd1ce..bc57a8e71c 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.IO; namespace osu.Game.Skinning @@ -50,7 +51,7 @@ namespace osu.Game.Skinning ID = DEFAULT_SKIN, Name = "osu!lazer", Creator = "team osu!", - InstantiationInfo = typeof(DefaultSkin).AssemblyQualifiedName, + InstantiationInfo = typeof(DefaultSkin).GetInvariantInstantiationInfo() }; public bool Equals(SkinInfo other) => other != null && ID == other.ID; From 27ca7d0f4fa24f8837607dab5f3009459e92b940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 12 May 2021 23:53:39 +0200 Subject: [PATCH 105/215] Actually annotate the correct ctor --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index b05c309e4e..f30130b1fb 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -11,12 +11,12 @@ namespace osu.Game.Skinning { public class DefaultLegacySkin : LegacySkin { - [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public DefaultLegacySkin(IResourceStore storage, IStorageResourceProvider resources) : this(Info, storage, resources) { } + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public DefaultLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources) : base(skin, storage, resources, string.Empty) { From 56bd8976666ee49318ab22d4dcfa86f5f7b627ae Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 04:29:27 +0200 Subject: [PATCH 106/215] Move `ShowIssueTypes` to `VerifyScreen` --- osu.Game/Screens/Edit/Verify/IssueList.cs | 14 ++------------ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 8 ++++++++ osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index befffdd9db..f2d3ef9dda 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } - public Dictionary> ShowType { get; set; } - public Bindable InterpretedDifficulty { get; set; } private IBeatmapVerifier rulesetVerifier; @@ -45,14 +43,6 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - // Reflects the user interface. Only types in this dictionary have configurable visibility. - ShowType = new Dictionary> - { - { IssueType.Warning, new Bindable(true) }, - { IssueType.Error, new Bindable(true) }, - { IssueType.Negligible, new Bindable(false) } - }; - generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); @@ -116,9 +106,9 @@ namespace osu.Game.Screens.Edit.Verify private IEnumerable filter(IEnumerable issues) { - foreach (IssueType issueType in ShowType.Keys) + foreach (var issueType in verify.ShowIssueType.Keys) { - if (!ShowType[issueType].Value) + if (!verify.ShowIssueType[issueType].Value) issues = issues.Where(issue => issue.Template.Type != issueType); } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index afd0c8760d..cf1a471714 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -17,6 +18,13 @@ namespace osu.Game.Screens.Edit.Verify public readonly Bindable InterpretedDifficulty = new Bindable(); + public readonly Dictionary> ShowIssueType = new Dictionary> + { + { IssueType.Warning, new Bindable(true) }, + { IssueType.Error, new Bindable(true) }, + { IssueType.Negligible, new Bindable(false) } + }; + public IssueList IssueList { get; private set; } public VerifyScreen() diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index ce755bdcb4..71f3740d86 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - foreach (IssueType issueType in verify.IssueList.ShowType.Keys) + foreach (IssueType issueType in verify.ShowIssueType.Keys) { var checkbox = new SettingsCheckbox { @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Verify LabelText = issueType.ToString() }; - checkbox.Current.BindTo(verify.IssueList.ShowType[issueType]); + checkbox.Current.BindTo(verify.ShowIssueType[issueType]); checkbox.Current.BindValueChanged(_ => verify.IssueList.Refresh()); Flow.Add(checkbox); } From 6806e40ad99b1bb6b561765d05858d11e6f5223a Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 04:30:40 +0200 Subject: [PATCH 107/215] Remove unnecessary local variable This now exists in `VerifyScreen`, which we can access from here. --- osu.Game/Screens/Edit/Verify/IssueList.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index f2d3ef9dda..19536401e9 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } - public Bindable InterpretedDifficulty { get; set; } - private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; @@ -46,8 +44,6 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); - InterpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); - RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] From dd8423c4c4350b5db811c8338d80235b2fc8a432 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 04:36:20 +0200 Subject: [PATCH 108/215] Set interpreted difficulty to correct default --- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index cf1a471714..963b77baa1 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -35,6 +35,9 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load() { + InterpretedDifficulty.Default = EditorBeatmap.BeatmapInfo.DifficultyRating; + InterpretedDifficulty.SetDefault(); + IssueList = new IssueList(); Child = new Container { From fbb76ba5989d10690411561dbec0d74b4c0151d6 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 04:50:32 +0200 Subject: [PATCH 109/215] Split `ShowIssueTypes` dict into hidden and configurable lists This way `VerifyScreen` is decoupled from which options `VisibilitySection` provides. Bindings are a bit less neat, though. --- osu.Game/Screens/Edit/Verify/IssueList.cs | 8 +------ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 8 +------ .../Screens/Edit/Verify/VisibilitySection.cs | 23 ++++++++++++++++--- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 19536401e9..22d410592e 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -102,13 +102,7 @@ namespace osu.Game.Screens.Edit.Verify private IEnumerable filter(IEnumerable issues) { - foreach (var issueType in verify.ShowIssueType.Keys) - { - if (!verify.ShowIssueType[issueType].Value) - issues = issues.Where(issue => issue.Template.Type != issueType); - } - - return issues; + return issues.Where(issue => !verify.HiddenIssueTypes.Contains(issue.Template.Type)); } } } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 963b77baa1..6d7a4a72e2 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -18,12 +17,7 @@ namespace osu.Game.Screens.Edit.Verify public readonly Bindable InterpretedDifficulty = new Bindable(); - public readonly Dictionary> ShowIssueType = new Dictionary> - { - { IssueType.Warning, new Bindable(true) }, - { IssueType.Error, new Bindable(true) }, - { IssueType.Negligible, new Bindable(false) } - }; + public readonly BindableList HiddenIssueTypes = new BindableList { IssueType.Negligible }; public IssueList IssueList { get; private set; } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index 71f3740d86..3698f51f66 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -14,12 +14,19 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } + private readonly IssueType[] configurableIssueTypes = + { + IssueType.Warning, + IssueType.Error, + IssueType.Negligible + }; + protected override string Header => "Visibility"; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - foreach (IssueType issueType in verify.ShowIssueType.Keys) + foreach (IssueType issueType in configurableIssueTypes) { var checkbox = new SettingsCheckbox { @@ -28,8 +35,18 @@ namespace osu.Game.Screens.Edit.Verify LabelText = issueType.ToString() }; - checkbox.Current.BindTo(verify.ShowIssueType[issueType]); - checkbox.Current.BindValueChanged(_ => verify.IssueList.Refresh()); + checkbox.Current.Default = !verify.HiddenIssueTypes.Contains(issueType); + checkbox.Current.SetDefault(); + checkbox.Current.BindValueChanged(state => + { + if (!state.NewValue) + verify.HiddenIssueTypes.Add(issueType); + else + verify.HiddenIssueTypes.Remove(issueType); + + verify.IssueList.Refresh(); + }); + Flow.Add(checkbox); } } From 5b0309296812f93f50aef02444367f7698cddb8c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 11:53:50 +0900 Subject: [PATCH 110/215] Fix possible test failure --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 69fbd56490..bba7e2b391 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set user ready", () => client.ChangeState(MultiplayerUserState.Ready)); AddStep("delete beatmap", () => beatmaps.Delete(importedSet)); - AddAssert("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle); + AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle); } [Test] From c8d21f2c3fe65f4c5add3e1429c907a370a61b6a Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 05:25:02 +0200 Subject: [PATCH 111/215] Isolate refreshing to `IssueList` --- osu.Game/Screens/Edit/Verify/IssueList.cs | 6 +++--- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 22d410592e..7eba50498c 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Edit.Verify new TriangleButton { Text = "Refresh", - Action = Refresh, + Action = refresh, Size = new Vector2(120, 40), Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -83,10 +83,10 @@ namespace osu.Game.Screens.Edit.Verify { base.LoadComplete(); - Refresh(); + verify.HiddenIssueTypes.BindCollectionChanged((o, s) => refresh(), runOnceImmediately: true); } - public void Refresh() + private void refresh() { var issues = generalVerifier.Run(beatmap, workingBeatmap.Value); diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index 3698f51f66..7ec3ecee07 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -43,8 +43,6 @@ namespace osu.Game.Screens.Edit.Verify verify.HiddenIssueTypes.Add(issueType); else verify.HiddenIssueTypes.Remove(issueType); - - verify.IssueList.Refresh(); }); Flow.Add(checkbox); From e86834b74060d5fc7e2e9314142316b74ccf821b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 05:25:20 +0200 Subject: [PATCH 112/215] Use local bound copy for `HiddenIssueTypes` --- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index 7ec3ecee07..48bf0523b1 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -26,6 +26,8 @@ namespace osu.Game.Screens.Edit.Verify [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { + var hiddenIssueTypes = verify.HiddenIssueTypes.GetBoundCopy(); + foreach (IssueType issueType in configurableIssueTypes) { var checkbox = new SettingsCheckbox @@ -35,14 +37,14 @@ namespace osu.Game.Screens.Edit.Verify LabelText = issueType.ToString() }; - checkbox.Current.Default = !verify.HiddenIssueTypes.Contains(issueType); + checkbox.Current.Default = !hiddenIssueTypes.Contains(issueType); checkbox.Current.SetDefault(); checkbox.Current.BindValueChanged(state => { if (!state.NewValue) - verify.HiddenIssueTypes.Add(issueType); + hiddenIssueTypes.Add(issueType); else - verify.HiddenIssueTypes.Remove(issueType); + hiddenIssueTypes.Remove(issueType); }); Flow.Add(checkbox); From 04c1585eb24ef0fa07333bc354c7122dc728e8df Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 05:38:45 +0200 Subject: [PATCH 113/215] Use more consistent lambda discards --- osu.Game/Screens/Edit/Verify/IssueList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 7eba50498c..03cd22ad54 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Edit.Verify { base.LoadComplete(); - verify.HiddenIssueTypes.BindCollectionChanged((o, s) => refresh(), runOnceImmediately: true); + verify.HiddenIssueTypes.BindCollectionChanged((_, __) => refresh(), runOnceImmediately: true); } private void refresh() From e80d8f69220509dc78a9fbdedd9eef797c27c7f1 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 05:46:47 +0200 Subject: [PATCH 114/215] Keep track of local bound copy --- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index 48bf0523b1..cb7deb8566 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Settings; @@ -21,12 +22,14 @@ namespace osu.Game.Screens.Edit.Verify IssueType.Negligible }; + private BindableList hiddenIssueTypes; + protected override string Header => "Visibility"; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) { - var hiddenIssueTypes = verify.HiddenIssueTypes.GetBoundCopy(); + hiddenIssueTypes = verify.HiddenIssueTypes.GetBoundCopy(); foreach (IssueType issueType in configurableIssueTypes) { From 6caf4e38790298f24837a0764fe5bfa778118674 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 12:57:28 +0900 Subject: [PATCH 115/215] Add xmldoc to `SkinnableInfo` --- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 2de3f1b729..2e9235df97 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Extensions; @@ -33,10 +34,15 @@ namespace osu.Game.Screens.Play.HUD public List Children { get; } = new List(); + [JsonConstructor] public SkinnableInfo() { } + /// + /// Construct a new instance populating all attributes from the provided drawable. + /// + /// The drawable which attributes should be sourced from. public SkinnableInfo(Drawable component) { Type = component.GetType(); @@ -47,13 +53,17 @@ namespace osu.Game.Screens.Play.HUD Anchor = component.Anchor; Origin = component.Origin; - if (component is Container container) + if (component is Container container) { - foreach (var child in container.Children.OfType().OfType()) + foreach (var child in container.OfType().OfType()) Children.Add(child.CreateSerialisedInformation()); } } + /// + /// Construct an instance of the drawable with all attributes applied. + /// + /// The new instance. public Drawable CreateInstance() { Drawable d = (Drawable)Activator.CreateInstance(Type); From ee0a6ba93e25df5425480f0427826b182055f6c4 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 05:59:49 +0200 Subject: [PATCH 116/215] Use local bound copy in `InterpretationSection` as well Else we're relying on the `VerifyScreen`'s bindable instance, and by extension the `VerifyScreen` instance itself. --- osu.Game/Screens/Edit/Verify/InterpretationSection.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs index 7991786542..1bdb528473 100644 --- a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; @@ -15,9 +16,13 @@ namespace osu.Game.Screens.Edit.Verify protected override string Header => "Interpretation"; + private Bindable interpretedDifficulty; + [BackgroundDependencyLoader] private void load() { + interpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); + var dropdown = new SettingsEnumDropdown { Anchor = Anchor.CentreLeft, @@ -25,7 +30,7 @@ namespace osu.Game.Screens.Edit.Verify TooltipText = "Affects checks that depend on difficulty level" }; - dropdown.Current.BindTo(verify.InterpretedDifficulty); + dropdown.Current.BindTo(interpretedDifficulty); Flow.Add(dropdown); } From fb305130deb9ba12229f36bb436635a04ac646d5 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 06:00:21 +0200 Subject: [PATCH 117/215] Also refresh when interpreted difficulty changes --- osu.Game/Screens/Edit/Verify/IssueList.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 03cd22ad54..407c1e3bc7 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -83,7 +83,10 @@ namespace osu.Game.Screens.Edit.Verify { base.LoadComplete(); - verify.HiddenIssueTypes.BindCollectionChanged((_, __) => refresh(), runOnceImmediately: true); + verify.InterpretedDifficulty.BindValueChanged(_ => refresh()); + verify.HiddenIssueTypes.BindCollectionChanged((_, __) => refresh()); + + refresh(); } private void refresh() From a38cb61b085329cf1712899674ace8c8de68a2d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:02:55 +0900 Subject: [PATCH 118/215] Remove duplicated call to `base.GetDrawableComponent` --- osu.Game/Skinning/DefaultSkin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 55f34ba1c6..d6ca5e9009 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -92,10 +92,10 @@ namespace osu.Game.Skinning return skinnableTargetWrapper; } - return null; + break; } - return base.GetDrawableComponent(component); + return null; } public override IBindable GetConfig(TLookup lookup) From 2bf8635ffd5d44210b6d6638482c089bbd3f611a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:03:23 +0900 Subject: [PATCH 119/215] Move field upwards in class --- osu.Game/Skinning/Editor/SkinBlueprintContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 9890b8e8b6..09bbf7ffd5 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -19,6 +19,8 @@ namespace osu.Game.Skinning.Editor { private readonly Drawable target; + private readonly List> targetComponents = new List>(); + public SkinBlueprintContainer(Drawable target) { this.target = target; @@ -30,8 +32,6 @@ namespace osu.Game.Skinning.Editor SelectedItems.BindTo(editor.SelectedComponents); } - private readonly List> targetComponents = new List>(); - protected override void LoadComplete() { base.LoadComplete(); From 469a7f5d2a07c434aface5cff1a1fdae987c5072 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:04:17 +0900 Subject: [PATCH 120/215] Reorder fields in `SkinEditor` --- osu.Game/Skinning/Editor/SkinEditor.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index a00557ee4e..72dacad310 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -22,19 +22,19 @@ namespace osu.Game.Skinning.Editor { public const double TRANSITION_DURATION = 500; + public readonly BindableList SelectedComponents = new BindableList(); + + protected override bool StartHidden => true; + private readonly Drawable targetScreen; private OsuTextFlowContainer headerText; - protected override bool StartHidden => true; - - public readonly BindableList SelectedComponents = new BindableList(); + private Bindable currentSkin; [Resolved] private SkinManager skins { get; set; } - private Bindable currentSkin; - [Resolved] private OsuColour colours { get; set; } From 992a052426da407f794bb3d241d30338d51a8ce8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:07:06 +0900 Subject: [PATCH 121/215] Remove stray comment --- osu.Game/Skinning/Skin.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 10481e4efd..1fb1e3b99f 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -39,8 +39,6 @@ namespace osu.Game.Skinning { SkinInfo = skin; - // may be null for default skin. - // we may want to move this to some kind of async operation in the future. foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget))) { From 47948d7b34c860e17d73579ac4ee6e91d69e3506 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 06:08:48 +0200 Subject: [PATCH 122/215] Set default for bindable in object initializer Fixes the CI failure. --- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index cb7deb8566..f942621d2a 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -37,10 +37,10 @@ namespace osu.Game.Screens.Edit.Verify { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - LabelText = issueType.ToString() + LabelText = issueType.ToString(), + Current = { Default = !hiddenIssueTypes.Contains(issueType) } }; - checkbox.Current.Default = !hiddenIssueTypes.Contains(issueType); checkbox.Current.SetDefault(); checkbox.Current.BindValueChanged(state => { From c93ed541f3571aa3d9991949d75f29b74fa46def Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:09:33 +0900 Subject: [PATCH 123/215] Add xmldoc and tidy up logic in `Skin` --- osu.Game/Skinning/Skin.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 1fb1e3b99f..64c848fbfa 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -61,11 +61,19 @@ namespace osu.Game.Skinning } } + /// + /// Remove all stored customisations for the provided target. + /// + /// The target container to reset. public void ResetDrawableTarget(SkinnableElementTargetContainer targetContainer) { DrawableComponentInfo.Remove(targetContainer.Target); } + /// + /// Update serialised information for the provided target. + /// + /// The target container to serialise to this skin. public void UpdateDrawableTarget(SkinnableElementTargetContainer targetContainer) { DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSerialisedChildren().ToArray(); @@ -76,10 +84,7 @@ namespace osu.Game.Skinning switch (component) { case SkinnableTargetComponent target: - - var skinnableTarget = target.Target; - - if (!DrawableComponentInfo.TryGetValue(skinnableTarget, out var skinnableInfo)) + if (!DrawableComponentInfo.TryGetValue(target.Target, out var skinnableInfo)) return null; return new SkinnableTargetWrapper From 581e7940c7331ab89f45cc6234089417c1e5bdd6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:13:22 +0900 Subject: [PATCH 124/215] Add xmldoc to `SkinnableElementTargetContainer` --- osu.Game/Skinning/SkinnableElementTargetContainer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Skinning/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableElementTargetContainer.cs index b900fdf3e0..f447800a33 100644 --- a/osu.Game/Skinning/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableElementTargetContainer.cs @@ -26,6 +26,9 @@ namespace osu.Game.Skinning Target = target; } + /// + /// Reload all components in this container from the current skin. + /// public void Reload() { ClearInternal(); @@ -43,6 +46,12 @@ namespace osu.Game.Skinning } } + /// + /// Add a new skinnable component to this target. + /// + /// The component to add. + /// Thrown when attempting to add an element to a target which is not supported by the current skin. + /// Thrown if the provided instance is not a . public void Add(ISkinnableComponent component) { if (content == null) From 3b862798e985edc8ff51ba99b4d2961ccdede31b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:14:49 +0900 Subject: [PATCH 125/215] Standardise naming of methods related to SkinnableInfo --- osu.Game/Extensions/DrawableExtensions.cs | 4 ++-- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 4 ++-- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Skinning/SkinnableElementTargetContainer.cs | 7 +++++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 0ec96a876e..2ac6e6ff22 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -46,9 +46,9 @@ namespace osu.Game.Extensions public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) => drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta); - public static SkinnableInfo CreateSerialisedInformation(this Drawable component) => new SkinnableInfo(component); + public static SkinnableInfo CreateSkinnableInfo(this Drawable component) => new SkinnableInfo(component); - public static void ApplySerialisedInformation(this Drawable component, SkinnableInfo info) + public static void ApplySkinnableInfo(this Drawable component, SkinnableInfo info) { // todo: can probably make this better via deserialisation directly using a common interface. component.Position = info.Position; diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 2e9235df97..678885d096 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play.HUD if (component is Container container) { foreach (var child in container.OfType().OfType()) - Children.Add(child.CreateSerialisedInformation()); + Children.Add(child.CreateSkinnableInfo()); } } @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play.HUD public Drawable CreateInstance() { Drawable d = (Drawable)Activator.CreateInstance(Type); - d.ApplySerialisedInformation(this); + d.ApplySkinnableInfo(this); return d; } } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 64c848fbfa..4890524b90 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -76,7 +76,7 @@ namespace osu.Game.Skinning /// The target container to serialise to this skin. public void UpdateDrawableTarget(SkinnableElementTargetContainer targetContainer) { - DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSerialisedChildren().ToArray(); + DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray(); } public virtual Drawable GetDrawableComponent(ISkinComponent component) diff --git a/osu.Game/Skinning/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableElementTargetContainer.cs index f447800a33..2aea8c0281 100644 --- a/osu.Game/Skinning/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableElementTargetContainer.cs @@ -64,8 +64,11 @@ namespace osu.Game.Skinning components.Add(component); } - public IEnumerable CreateSerialisedChildren() => - components.Select(d => ((Drawable)d).CreateSerialisedInformation()); + /// + /// Serialise all children as . + /// + /// The serialised content. + public IEnumerable CreateSkinnableInfo() => components.Select(d => ((Drawable)d).CreateSkinnableInfo()); protected override void SkinChanged(ISkinSource skin, bool allowFallback) { From db19617b8b69a9b9102b8594ed34d881f36d5ede Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:16:20 +0900 Subject: [PATCH 126/215] Add `JsonConstructor` attribute to `SkinnableTargetWrapper` --- osu.Game/Skinning/SkinnableTargetWrapper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index 0d16775f51..2e2ce32a6f 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using Newtonsoft.Json; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,6 +12,7 @@ namespace osu.Game.Skinning /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type (for consumption via . /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// + [Serializable] public class SkinnableTargetWrapper : Container, ISkinSerialisable { private readonly Action applyDefaults; @@ -25,6 +27,7 @@ namespace osu.Game.Skinning this.applyDefaults = applyDefaults; } + [JsonConstructor] public SkinnableTargetWrapper() { RelativeSizeAxes = Axes.Both; From 23e284b8b31cc59189382d16cb06a72cf16fb00d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:34:03 +0900 Subject: [PATCH 127/215] Change default skin editor shortcut to Ctrl+Shift+S Avoids a conflict with song select's random rewind functionality. As mentioned in #12776. --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index ce945f3bf8..c8227c0887 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleBeatmapListing), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), - new KeyBinding(new[] { InputKey.Shift, InputKey.F2 }, GlobalAction.ToggleSkinEditor), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, GlobalAction.ToggleSkinEditor), new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), From cdcbaf4291e4bbf20b552a305244be92336dbe80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:45:10 +0900 Subject: [PATCH 128/215] Tidy up specification of `SettingsSection` --- .../Screens/Edit/EditorRoundedScreenSettingsSection.cs | 7 ++++--- osu.Game/Screens/Edit/Verify/InterpretationSection.cs | 2 +- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs b/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs index 87ed98a439..e17114ebcb 100644 --- a/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs +++ b/osu.Game/Screens/Edit/EditorRoundedScreenSettingsSection.cs @@ -15,8 +15,9 @@ namespace osu.Game.Screens.Edit { private const int header_height = 50; - protected FillFlowContainer Flow; - protected abstract string Header { get; } + protected abstract string HeaderText { get; } + + protected FillFlowContainer Flow { get; private set; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) @@ -36,7 +37,7 @@ namespace osu.Game.Screens.Edit { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Text = Header, + Text = HeaderText, Font = new FontUsage(size: 25, weight: "bold") } }, diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs index 1bdb528473..4dca94aacf 100644 --- a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } - protected override string Header => "Interpretation"; + protected override string HeaderText => "Interpretation"; private Bindable interpretedDifficulty; diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index f942621d2a..7ebfe586a7 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit.Verify private BindableList hiddenIssueTypes; - protected override string Header => "Visibility"; + protected override string HeaderText => "Visibility"; [BackgroundDependencyLoader] private void load(OverlayColourProvider colours) From c6648112e59f8a5c3429af35f72b99d8ceceb2c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:51:41 +0900 Subject: [PATCH 129/215] Simplify binding flow in `InterpretationSection` --- .../Screens/Edit/Verify/InterpretationSection.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs index 4dca94aacf..7de87737f5 100644 --- a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; @@ -16,23 +15,16 @@ namespace osu.Game.Screens.Edit.Verify protected override string HeaderText => "Interpretation"; - private Bindable interpretedDifficulty; - [BackgroundDependencyLoader] private void load() { - interpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); - - var dropdown = new SettingsEnumDropdown + Flow.Add(new SettingsEnumDropdown { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - TooltipText = "Affects checks that depend on difficulty level" - }; - - dropdown.Current.BindTo(interpretedDifficulty); - - Flow.Add(dropdown); + TooltipText = "Affects checks that depend on difficulty level", + Current = verify.InterpretedDifficulty.GetBoundCopy() + }); } } } From b81f86bd4d9c088dce4a7c49fb16b768ccc3e669 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 13:53:27 +0900 Subject: [PATCH 130/215] Move DI resolution to inside BDL parameters --- osu.Game/Screens/Edit/Verify/InterpretationSection.cs | 5 +---- osu.Game/Screens/Edit/Verify/VisibilitySection.cs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs index 7de87737f5..9548f8aaa9 100644 --- a/osu.Game/Screens/Edit/Verify/InterpretationSection.cs +++ b/osu.Game/Screens/Edit/Verify/InterpretationSection.cs @@ -10,13 +10,10 @@ namespace osu.Game.Screens.Edit.Verify { internal class InterpretationSection : EditorRoundedScreenSettingsSection { - [Resolved] - private VerifyScreen verify { get; set; } - protected override string HeaderText => "Interpretation"; [BackgroundDependencyLoader] - private void load() + private void load(VerifyScreen verify) { Flow.Add(new SettingsEnumDropdown { diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs index 7ebfe586a7..d049436376 100644 --- a/osu.Game/Screens/Edit/Verify/VisibilitySection.cs +++ b/osu.Game/Screens/Edit/Verify/VisibilitySection.cs @@ -12,9 +12,6 @@ namespace osu.Game.Screens.Edit.Verify { internal class VisibilitySection : EditorRoundedScreenSettingsSection { - [Resolved] - private VerifyScreen verify { get; set; } - private readonly IssueType[] configurableIssueTypes = { IssueType.Warning, @@ -27,7 +24,7 @@ namespace osu.Game.Screens.Edit.Verify protected override string HeaderText => "Visibility"; [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) + private void load(OverlayColourProvider colours, VerifyScreen verify) { hiddenIssueTypes = verify.HiddenIssueTypes.GetBoundCopy(); From e0e9106921fc1b4c30c19c56645e19cd696f455f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 14:54:52 +0900 Subject: [PATCH 131/215] Enable autoplay in skin editor tests --- osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs | 2 ++ .../Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 73da76df78..a0b27755b7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SkinManager skinManager { get; set; } + protected override bool Autoplay => true; + [SetUpSteps] public override void SetUpSteps() { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs index c7c93b8892..245e190b1f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditorMultipleSkins.cs @@ -1,13 +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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; @@ -32,12 +30,13 @@ namespace osu.Game.Tests.Visual.Gameplay SetContents(() => { var ruleset = new OsuRuleset(); + var mods = new[] { ruleset.GetAutoplayMod() }; var working = CreateWorkingBeatmap(ruleset.RulesetInfo); - var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo); + var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); + var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap, mods); - var hudOverlay = new HUDOverlay(drawableRuleset, Array.Empty()) + var hudOverlay = new HUDOverlay(drawableRuleset, mods) { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 4eeeaf6a1aa9588932c2c096c40232d6bb5527d4 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 07:57:32 +0200 Subject: [PATCH 132/215] Keep track of local bound copy --- osu.Game/Screens/Edit/Verify/IssueList.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index 2a90834537..f150f4175b 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } + private Bindable interpretedDifficulty; + private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; private BeatmapVerifierContext context; @@ -45,8 +47,10 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); + interpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); + context = new BeatmapVerifierContext(workingBeatmap.Value); - context.InterpretedDifficulty.BindTo(verify.InterpretedDifficulty.GetBoundCopy()); + context.InterpretedDifficulty.BindTo(interpretedDifficulty); RelativeSizeAxes = Axes.Both; From b37cb3bdbeadefb2bba7a71575e8be9c535c54d3 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 09:00:30 +0200 Subject: [PATCH 133/215] Change interpreted difficulty from bindable to regular value There's no reason for why checks would need this to be bindable. A 1-directional binding is more appropriate. --- osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs | 5 ++--- osu.Game/Screens/Edit/Verify/IssueList.cs | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs index 59d43ba3d6..76b74cb993 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.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 osu.Framework.Bindables; using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit @@ -20,12 +19,12 @@ namespace osu.Game.Rulesets.Edit /// /// The difficulty level which the current beatmap is considered to be. /// - public readonly Bindable InterpretedDifficulty; + public DifficultyRating InterpretedDifficulty; public BeatmapVerifierContext(IWorkingBeatmap workingBeatmap, DifficultyRating difficultyRating = DifficultyRating.ExpertPlus) { WorkingBeatmap = workingBeatmap; - InterpretedDifficulty = new Bindable(difficultyRating); + InterpretedDifficulty = difficultyRating; } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index f150f4175b..abf22ead10 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private VerifyScreen verify { get; set; } - private Bindable interpretedDifficulty; - private IBeatmapVerifier rulesetVerifier; private BeatmapVerifier generalVerifier; private BeatmapVerifierContext context; @@ -47,10 +45,11 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); - interpretedDifficulty = verify.InterpretedDifficulty.GetBoundCopy(); - - context = new BeatmapVerifierContext(workingBeatmap.Value); - context.InterpretedDifficulty.BindTo(interpretedDifficulty); + context = new BeatmapVerifierContext(workingBeatmap.Value, verify.InterpretedDifficulty.Value); + verify.InterpretedDifficulty.BindValueChanged(change => + { + context.InterpretedDifficulty = change.NewValue; + }); RelativeSizeAxes = Axes.Both; From 5818ed4c8c66bb749a83e700c16fde831ba56b4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 16:41:36 +0900 Subject: [PATCH 134/215] Remove unused DI resolution --- osu.Game/Skinning/LegacyAccuracyCounter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index 3eea5d22d5..493107172f 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -12,9 +12,6 @@ namespace osu.Game.Skinning { public class LegacyAccuracyCounter : GameplayAccuracyCounter, ISkinnableComponent { - [Resolved] - private ISkinSource skin { get; set; } - public LegacyAccuracyCounter() { Anchor = Anchor.TopRight; From 19223ba01359b87ebb55b838d47fe6846fd58db4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 16:42:13 +0900 Subject: [PATCH 135/215] Remove left-over debug logging --- osu.Game/Skinning/SkinManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 91f3d0c7cf..fbe23482d7 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -194,9 +194,6 @@ namespace osu.Game.Skinning ReplaceFile(skin.SkinInfo, oldFile, streamContent, oldFile.Filename); else AddFile(skin.SkinInfo, streamContent, filename); - - Logger.Log($"Saving out {filename} with {json.Length} bytes"); - Logger.Log(json); } } } From 9dfa48b22ed523f67bc4b29e7a497127b28d6280 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 16:42:43 +0900 Subject: [PATCH 136/215] Fix incorrect exception text --- osu.Game/Skinning/SkinnableElementTargetContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableElementTargetContainer.cs index 2aea8c0281..c68dce4109 100644 --- a/osu.Game/Skinning/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableElementTargetContainer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Skinning throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); if (!(component is Drawable drawable)) - throw new ArgumentException("Provided argument must be of type {nameof(ISkinnableComponent)}.", nameof(drawable)); + throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable)); content.Add(drawable); components.Add(component); From dd6a06a302a7f38c3889829068589b1260cf944c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 16:43:42 +0900 Subject: [PATCH 137/215] Reword xmldoc to read better --- osu.Game/Skinning/SkinnableTargetWrapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index 2e2ce32a6f..d8ad008448 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -20,7 +20,7 @@ namespace osu.Game.Skinning /// /// Construct a wrapper with defaults that should be applied once. /// - /// A function with default to apply after the initial layout (ie. consuming autosize) + /// A function to apply the default layout. public SkinnableTargetWrapper(Action applyDefaults) : this() { From cdcd31b546ac41806c0f7559d65dd9b24e6323d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:03:17 +0900 Subject: [PATCH 138/215] Replace `ISkinSerialisable` with `IsEditable` property --- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 4 ++-- .../Skinning/Editor/SkinBlueprintContainer.cs | 8 ++++++++ osu.Game/Skinning/Editor/SkinComponentToolbox.cs | 3 +++ osu.Game/Skinning/ISkinSerialisable.cs | 15 --------------- osu.Game/Skinning/ISkinnableComponent.cs | 8 +++++++- osu.Game/Skinning/SkinManager.cs | 1 - osu.Game/Skinning/SkinnableTargetWrapper.cs | 4 +++- 7 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 osu.Game/Skinning/ISkinSerialisable.cs diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index 678885d096..a7c5c33f7d 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { /// - /// Serialised information governing custom changes to an . + /// Serialised information governing custom changes to an . /// [Serializable] public class SkinnableInfo : IJsonSerializable @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Play.HUD if (component is Container container) { - foreach (var child in container.OfType().OfType()) + foreach (var child in container.OfType().OfType()) Children.Add(child.CreateSkinnableInfo()); } } diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 09bbf7ffd5..31f89ad0c2 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -81,6 +81,14 @@ namespace osu.Game.Skinning.Editor } } + protected override void AddBlueprintFor(ISkinnableComponent item) + { + if (!item.IsEditable) + return; + + base.AddBlueprintFor(item); + } + protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); protected override SelectionBlueprint CreateBlueprintFor(ISkinnableComponent component) diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index a000204062..068b2058a6 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -78,6 +78,9 @@ namespace osu.Game.Skinning.Editor Debug.Assert(instance != null); + if (!((ISkinnableComponent)instance).IsEditable) + return null; + return new ToolboxComponentButton(instance); } catch diff --git a/osu.Game/Skinning/ISkinSerialisable.cs b/osu.Game/Skinning/ISkinSerialisable.cs deleted file mode 100644 index d1777512af..0000000000 --- a/osu.Game/Skinning/ISkinSerialisable.cs +++ /dev/null @@ -1,15 +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.Graphics; - -namespace osu.Game.Skinning -{ - /// - /// Denotes a drawable component which should be serialised as part of a skin. - /// Use for components which should be mutable by the user / editor. - /// - public interface ISkinSerialisable : IDrawable - { - } -} diff --git a/osu.Game/Skinning/ISkinnableComponent.cs b/osu.Game/Skinning/ISkinnableComponent.cs index e44c7be13d..65ac2d5849 100644 --- a/osu.Game/Skinning/ISkinnableComponent.cs +++ b/osu.Game/Skinning/ISkinnableComponent.cs @@ -1,12 +1,18 @@ // 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.Graphics; + namespace osu.Game.Skinning { /// /// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications. /// - public interface ISkinnableComponent : ISkinSerialisable + public interface ISkinnableComponent : IDrawable { + /// + /// Whether this component should be editable by an end user. + /// + bool IsEditable => true; } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fbe23482d7..7b27a7a816 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -19,7 +19,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; -using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index d8ad008448..28e8d585fc 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -13,8 +13,10 @@ namespace osu.Game.Skinning /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// [Serializable] - public class SkinnableTargetWrapper : Container, ISkinSerialisable + public class SkinnableTargetWrapper : Container, ISkinnableComponent { + public bool IsEditable => false; + private readonly Action applyDefaults; /// From 7921dc7ece64f04d37e8e96630a4d0f677d29fe9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:06:00 +0900 Subject: [PATCH 139/215] Rename `ISkinnableComponent` to `ISkinnableDrawable` --- .../Play/HUD/DefaultAccuracyCounter.cs | 2 +- .../Screens/Play/HUD/DefaultComboCounter.cs | 2 +- .../Screens/Play/HUD/DefaultHealthDisplay.cs | 2 +- .../Screens/Play/HUD/DefaultScoreCounter.cs | 2 +- .../Screens/Play/HUD/LegacyComboCounter.cs | 2 +- osu.Game/Screens/Play/HUD/SkinnableInfo.cs | 4 ++-- osu.Game/Skinning/Editor/SkinBlueprint.cs | 4 ++-- .../Skinning/Editor/SkinBlueprintContainer.cs | 20 +++++++++---------- .../Skinning/Editor/SkinComponentToolbox.cs | 4 ++-- osu.Game/Skinning/Editor/SkinEditor.cs | 4 ++-- .../Skinning/Editor/SkinSelectionHandler.cs | 10 +++++----- ...ableComponent.cs => ISkinnableDrawable.cs} | 2 +- osu.Game/Skinning/ISkinnableTarget.cs | 6 +++--- osu.Game/Skinning/LegacyAccuracyCounter.cs | 2 +- osu.Game/Skinning/LegacyHealthDisplay.cs | 2 +- osu.Game/Skinning/LegacyScoreCounter.cs | 2 +- .../SkinnableElementTargetContainer.cs | 8 ++++---- osu.Game/Skinning/SkinnableTargetWrapper.cs | 2 +- 18 files changed, 40 insertions(+), 40 deletions(-) rename osu.Game/Skinning/{ISkinnableComponent.cs => ISkinnableDrawable.cs} (90%) diff --git a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs index d8dff89b29..45ba05e036 100644 --- a/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultAccuracyCounter.cs @@ -7,7 +7,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class DefaultAccuracyCounter : GameplayAccuracyCounter, ISkinnableComponent + public class DefaultAccuracyCounter : GameplayAccuracyCounter, ISkinnableDrawable { [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } diff --git a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs index 13bd045fc0..c4575c5ad0 100644 --- a/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultComboCounter.cs @@ -12,7 +12,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class DefaultComboCounter : RollingCounter, ISkinnableComponent + public class DefaultComboCounter : RollingCounter, ISkinnableDrawable { [Resolved(canBeNull: true)] private HUDOverlay hud { get; set; } diff --git a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs index 241777244b..ed297f0ffc 100644 --- a/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/DefaultHealthDisplay.cs @@ -17,7 +17,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class DefaultHealthDisplay : HealthDisplay, IHasAccentColour, ISkinnableComponent + public class DefaultHealthDisplay : HealthDisplay, IHasAccentColour, ISkinnableDrawable { /// /// The base opacity of the glow. diff --git a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs index bd18050dfb..16e3642181 100644 --- a/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/DefaultScoreCounter.cs @@ -8,7 +8,7 @@ using osu.Game.Skinning; namespace osu.Game.Screens.Play.HUD { - public class DefaultScoreCounter : GameplayScoreCounter, ISkinnableComponent + public class DefaultScoreCounter : GameplayScoreCounter, ISkinnableDrawable { public DefaultScoreCounter() : base(6) diff --git a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs index 2565faf423..d64513d41e 100644 --- a/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/LegacyComboCounter.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Play.HUD /// /// Uses the 'x' symbol and has a pop-out effect while rolling over. /// - public class LegacyComboCounter : CompositeDrawable, ISkinnableComponent + public class LegacyComboCounter : CompositeDrawable, ISkinnableDrawable { public Bindable Current { get; } = new BindableInt { MinValue = 0, }; diff --git a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs index a7c5c33f7d..e08044b14c 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableInfo.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableInfo.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Screens.Play.HUD { /// - /// Serialised information governing custom changes to an . + /// Serialised information governing custom changes to an . /// [Serializable] public class SkinnableInfo : IJsonSerializable @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Play.HUD if (component is Container container) { - foreach (var child in container.OfType().OfType()) + foreach (var child in container.OfType().OfType()) Children.Add(child.CreateSkinnableInfo()); } } diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index b8dfdbad0a..4be9299699 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Skinning.Editor { - public class SkinBlueprint : SelectionBlueprint + public class SkinBlueprint : SelectionBlueprint { private Container box; @@ -26,7 +26,7 @@ namespace osu.Game.Skinning.Editor protected override bool ShouldBeAlive => (drawable.IsAlive && Item.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected); - public SkinBlueprint(ISkinnableComponent component) + public SkinBlueprint(ISkinnableDrawable component) : base(component) { } diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index 31f89ad0c2..c0cc2ab40e 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -15,11 +15,11 @@ using osu.Game.Screens.Edit.Compose.Components; namespace osu.Game.Skinning.Editor { - public class SkinBlueprintContainer : BlueprintContainer + public class SkinBlueprintContainer : BlueprintContainer { private readonly Drawable target; - private readonly List> targetComponents = new List>(); + private readonly List> targetComponents = new List>(); public SkinBlueprintContainer(Drawable target) { @@ -49,7 +49,7 @@ namespace osu.Game.Skinning.Editor foreach (var targetContainer in targetContainers) { - var bindableList = new BindableList { BindTarget = targetContainer.Components }; + var bindableList = new BindableList { BindTarget = targetContainer.Components }; bindableList.BindCollectionChanged(componentsChanged, true); targetComponents.Add(bindableList); @@ -61,27 +61,27 @@ namespace osu.Game.Skinning.Editor switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (var item in e.NewItems.Cast()) + foreach (var item in e.NewItems.Cast()) AddBlueprintFor(item); break; case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Reset: - foreach (var item in e.OldItems.Cast()) + foreach (var item in e.OldItems.Cast()) RemoveBlueprintFor(item); break; case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems.Cast()) + foreach (var item in e.OldItems.Cast()) RemoveBlueprintFor(item); - foreach (var item in e.NewItems.Cast()) + foreach (var item in e.NewItems.Cast()) AddBlueprintFor(item); break; } } - protected override void AddBlueprintFor(ISkinnableComponent item) + protected override void AddBlueprintFor(ISkinnableDrawable item) { if (!item.IsEditable) return; @@ -89,9 +89,9 @@ namespace osu.Game.Skinning.Editor base.AddBlueprintFor(item); } - protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); + protected override SelectionHandler CreateSelectionHandler() => new SkinSelectionHandler(); - protected override SelectionBlueprint CreateBlueprintFor(ISkinnableComponent component) + protected override SelectionBlueprint CreateBlueprintFor(ISkinnableDrawable component) => new SkinBlueprint(component); } } diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index 068b2058a6..8536cba139 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -56,7 +56,7 @@ namespace osu.Game.Skinning.Editor Spacing = new Vector2(20) }; - var skinnableTypes = typeof(OsuGame).Assembly.GetTypes().Where(t => typeof(ISkinnableComponent).IsAssignableFrom(t)).ToArray(); + var skinnableTypes = typeof(OsuGame).Assembly.GetTypes().Where(t => typeof(ISkinnableDrawable).IsAssignableFrom(t)).ToArray(); foreach (var type in skinnableTypes) { @@ -78,7 +78,7 @@ namespace osu.Game.Skinning.Editor Debug.Assert(instance != null); - if (!((ISkinnableComponent)instance).IsEditable) + if (!((ISkinnableDrawable)instance).IsEditable) return null; return new ToolboxComponentButton(instance); diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 72dacad310..bf9a6464d0 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Skinning.Editor { public const double TRANSITION_DURATION = 500; - public readonly BindableList SelectedComponents = new BindableList(); + public readonly BindableList SelectedComponents = new BindableList(); protected override bool StartHidden => true; @@ -165,7 +165,7 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { - if (!(Activator.CreateInstance(type) is ISkinnableComponent component)) + if (!(Activator.CreateInstance(type) is ISkinnableDrawable component)) throw new InvalidOperationException("Attempted to instantiate a component for placement which was not an {typeof(ISkinnableComponent)}."); getTarget(SkinnableTarget.MainHUDComponents)?.Add(component); diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index cf5ece03e9..8ca98c794f 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -14,7 +14,7 @@ using osuTK; namespace osu.Game.Skinning.Editor { - public class SkinSelectionHandler : SelectionHandler + public class SkinSelectionHandler : SelectionHandler { public override bool HandleRotation(float angle) { @@ -36,7 +36,7 @@ namespace osu.Game.Skinning.Editor return true; } - public override bool HandleMovement(MoveSelectionEvent moveEvent) + public override bool HandleMovement(MoveSelectionEvent moveEvent) { foreach (var c in SelectedBlueprints) { @@ -57,7 +57,7 @@ namespace osu.Game.Skinning.Editor SelectionBox.CanReverse = false; } - protected override void DeleteItems(IEnumerable items) + protected override void DeleteItems(IEnumerable items) { foreach (var i in items) { @@ -66,7 +66,7 @@ namespace osu.Game.Skinning.Editor } } - protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) + protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) { yield return new OsuMenuItem("Anchor") { @@ -131,7 +131,7 @@ namespace osu.Game.Skinning.Editor public class AnchorMenuItem : TernaryStateMenuItem { - public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) + public AnchorMenuItem(Anchor anchor, IEnumerable> selection, Action action) : base(anchor.ToString(), getNextState, MenuItemType.Standard, action) { } diff --git a/osu.Game/Skinning/ISkinnableComponent.cs b/osu.Game/Skinning/ISkinnableDrawable.cs similarity index 90% rename from osu.Game/Skinning/ISkinnableComponent.cs rename to osu.Game/Skinning/ISkinnableDrawable.cs index 65ac2d5849..d42b6f71b0 100644 --- a/osu.Game/Skinning/ISkinnableComponent.cs +++ b/osu.Game/Skinning/ISkinnableDrawable.cs @@ -8,7 +8,7 @@ namespace osu.Game.Skinning /// /// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications. /// - public interface ISkinnableComponent : IDrawable + public interface ISkinnableDrawable : IDrawable { /// /// Whether this component should be editable by an end user. diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index ab3c24a1e2..3773b5be24 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -6,7 +6,7 @@ using osu.Framework.Bindables; namespace osu.Game.Skinning { /// - /// Denotes a container which can house s. + /// Denotes a container which can house s. /// public interface ISkinnableTarget { @@ -18,7 +18,7 @@ namespace osu.Game.Skinning /// /// A bindable list of components which are being tracked by this skinnable target. /// - IBindableList Components { get; } + IBindableList Components { get; } /// /// Reload this target from the current skin. @@ -28,6 +28,6 @@ namespace osu.Game.Skinning /// /// Add the provided item to this target. /// - void Add(ISkinnableComponent drawable); + void Add(ISkinnableDrawable drawable); } } diff --git a/osu.Game/Skinning/LegacyAccuracyCounter.cs b/osu.Game/Skinning/LegacyAccuracyCounter.cs index 493107172f..16562d9571 100644 --- a/osu.Game/Skinning/LegacyAccuracyCounter.cs +++ b/osu.Game/Skinning/LegacyAccuracyCounter.cs @@ -10,7 +10,7 @@ using osuTK; namespace osu.Game.Skinning { - public class LegacyAccuracyCounter : GameplayAccuracyCounter, ISkinnableComponent + public class LegacyAccuracyCounter : GameplayAccuracyCounter, ISkinnableDrawable { public LegacyAccuracyCounter() { diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index dfb6ca1a64..c601adc3a0 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -16,7 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Skinning { - public class LegacyHealthDisplay : HealthDisplay, ISkinnableComponent + public class LegacyHealthDisplay : HealthDisplay, ISkinnableDrawable { private const double epic_cutoff = 0.5; diff --git a/osu.Game/Skinning/LegacyScoreCounter.cs b/osu.Game/Skinning/LegacyScoreCounter.cs index 8aa48da453..64ea03d59c 100644 --- a/osu.Game/Skinning/LegacyScoreCounter.cs +++ b/osu.Game/Skinning/LegacyScoreCounter.cs @@ -8,7 +8,7 @@ using osuTK; namespace osu.Game.Skinning { - public class LegacyScoreCounter : GameplayScoreCounter, ISkinnableComponent + public class LegacyScoreCounter : GameplayScoreCounter, ISkinnableDrawable { protected override double RollingDuration => 1000; protected override Easing RollingEasing => Easing.Out; diff --git a/osu.Game/Skinning/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableElementTargetContainer.cs index c68dce4109..2a76d3bf7c 100644 --- a/osu.Game/Skinning/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableElementTargetContainer.cs @@ -17,9 +17,9 @@ namespace osu.Game.Skinning public SkinnableTarget Target { get; } - public IBindableList Components => components; + public IBindableList Components => components; - private readonly BindableList components = new BindableList(); + private readonly BindableList components = new BindableList(); public SkinnableElementTargetContainer(SkinnableTarget target) { @@ -41,7 +41,7 @@ namespace osu.Game.Skinning LoadComponentAsync(content, wrapper => { AddInternal(wrapper); - components.AddRange(wrapper.Children.OfType()); + components.AddRange(wrapper.Children.OfType()); }); } } @@ -52,7 +52,7 @@ namespace osu.Game.Skinning /// The component to add. /// Thrown when attempting to add an element to a target which is not supported by the current skin. /// Thrown if the provided instance is not a . - public void Add(ISkinnableComponent component) + public void Add(ISkinnableDrawable component) { if (content == null) throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index 28e8d585fc..58ecac71ab 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -13,7 +13,7 @@ namespace osu.Game.Skinning /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// [Serializable] - public class SkinnableTargetWrapper : Container, ISkinnableComponent + public class SkinnableTargetWrapper : Container, ISkinnableDrawable { public bool IsEditable => false; From 106fa97a11080329a6097430590866d827dfe1a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:07:38 +0900 Subject: [PATCH 140/215] Rename `SkinnableElementTargetContainer` to `SkinnableTargetContainer` --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- osu.Game/Skinning/Editor/SkinEditor.cs | 4 ++-- osu.Game/Skinning/Skin.cs | 4 ++-- ...eElementTargetContainer.cs => SkinnableTargetContainer.cs} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Skinning/{SkinnableElementTargetContainer.cs => SkinnableTargetContainer.cs} (94%) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 887346b5df..a10e91dae8 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play private bool holdingForHUD; - private readonly SkinnableElementTargetContainer mainComponents; + private readonly SkinnableTargetContainer mainComponents; private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - mainComponents = new SkinnableElementTargetContainer(SkinnableTarget.MainHUDComponents) + mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents) { RelativeSizeAxes = Axes.Both, }, diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index bf9a6464d0..67285987ef 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -181,7 +181,7 @@ namespace osu.Game.Skinning.Editor private void revert() { - SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); + SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) { @@ -194,7 +194,7 @@ namespace osu.Game.Skinning.Editor private void save() { - SkinnableElementTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); + SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); foreach (var t in targetContainers) currentSkin.Value.UpdateDrawableTarget(t); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 4890524b90..3a16cb5818 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning /// Remove all stored customisations for the provided target. /// /// The target container to reset. - public void ResetDrawableTarget(SkinnableElementTargetContainer targetContainer) + public void ResetDrawableTarget(SkinnableTargetContainer targetContainer) { DrawableComponentInfo.Remove(targetContainer.Target); } @@ -74,7 +74,7 @@ namespace osu.Game.Skinning /// Update serialised information for the provided target. /// /// The target container to serialise to this skin. - public void UpdateDrawableTarget(SkinnableElementTargetContainer targetContainer) + public void UpdateDrawableTarget(SkinnableTargetContainer targetContainer) { DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray(); } diff --git a/osu.Game/Skinning/SkinnableElementTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs similarity index 94% rename from osu.Game/Skinning/SkinnableElementTargetContainer.cs rename to osu.Game/Skinning/SkinnableTargetContainer.cs index 2a76d3bf7c..daf54277fd 100644 --- a/osu.Game/Skinning/SkinnableElementTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -11,7 +11,7 @@ using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning { - public class SkinnableElementTargetContainer : SkinReloadableDrawable, ISkinnableTarget + public class SkinnableTargetContainer : SkinReloadableDrawable, ISkinnableTarget { private SkinnableTargetWrapper content; @@ -21,7 +21,7 @@ namespace osu.Game.Skinning private readonly BindableList components = new BindableList(); - public SkinnableElementTargetContainer(SkinnableTarget target) + public SkinnableTargetContainer(SkinnableTarget target) { Target = target; } From 0959e7156a27a29b4a7a8fa041e418e4cc84f577 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:22:05 +0900 Subject: [PATCH 141/215] Remove outdated TODO --- osu.Game/Skinning/SkinManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 7b27a7a816..5793edda30 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -180,7 +180,6 @@ namespace osu.Game.Skinning foreach (var drawableInfo in skin.DrawableComponentInfo) { - // todo: the OfType() call can be removed with better IDrawable support. string json = JsonConvert.SerializeObject(drawableInfo.Value, new JsonSerializerSettings { Formatting = Formatting.Indented }); using (var streamContent = new MemoryStream(Encoding.UTF8.GetBytes(json))) From 3ea469813c8cc95396d020a1f145cf0d2c67040e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:25:51 +0900 Subject: [PATCH 142/215] Use interface in place of `SkinnableTargetContainer` --- osu.Game/Skinning/ISkinnableTarget.cs | 11 +++++++++++ osu.Game/Skinning/Skin.cs | 4 ++-- osu.Game/Skinning/SkinnableTargetContainer.cs | 9 --------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 3773b5be24..ba3a9fe228 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -1,7 +1,12 @@ // 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 osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Extensions; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning { @@ -20,6 +25,12 @@ namespace osu.Game.Skinning /// IBindableList Components { get; } + /// + /// Serialise all children as . + /// + /// The serialised content. + IEnumerable CreateSkinnableInfo() => Components.Select(d => ((Drawable)d).CreateSkinnableInfo()); + /// /// Reload this target from the current skin. /// diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 3a16cb5818..0ceca3925e 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -65,7 +65,7 @@ namespace osu.Game.Skinning /// Remove all stored customisations for the provided target. /// /// The target container to reset. - public void ResetDrawableTarget(SkinnableTargetContainer targetContainer) + public void ResetDrawableTarget(ISkinnableTarget targetContainer) { DrawableComponentInfo.Remove(targetContainer.Target); } @@ -74,7 +74,7 @@ namespace osu.Game.Skinning /// Update serialised information for the provided target. /// /// The target container to serialise to this skin. - public void UpdateDrawableTarget(SkinnableTargetContainer targetContainer) + public void UpdateDrawableTarget(ISkinnableTarget targetContainer) { DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray(); } diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index daf54277fd..52e86f5d86 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -2,12 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Extensions; -using osu.Game.Screens.Play.HUD; namespace osu.Game.Skinning { @@ -64,12 +61,6 @@ namespace osu.Game.Skinning components.Add(component); } - /// - /// Serialise all children as . - /// - /// The serialised content. - public IEnumerable CreateSkinnableInfo() => components.Select(d => ((Drawable)d).CreateSkinnableInfo()); - protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); From ebce3fd3c7d408541b39b2fa37df45d82d057a08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:29:11 +0900 Subject: [PATCH 143/215] Use `ScheduleAfterChildren` to better match comment --- osu.Game/Skinning/SkinnableTargetWrapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index 58ecac71ab..664e8da480 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -40,7 +40,7 @@ namespace osu.Game.Skinning base.LoadComplete(); // schedule is required to allow children to run their LoadComplete and take on their correct sizes. - Schedule(() => applyDefaults?.Invoke(this)); + ScheduleAfterChildren(() => applyDefaults?.Invoke(this)); } } } From 1cda55393ed7b25874c8a3fe7bf77dc33b136035 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 17:51:57 +0900 Subject: [PATCH 144/215] Add aspect ratio locking and flip support to skin editor --- .../Skinning/Editor/SkinSelectionHandler.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index ad783a9c0e..3dc0ca340b 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Extensions; @@ -36,6 +37,20 @@ namespace osu.Game.Skinning.Editor return true; } + public override bool HandleFlip(Direction direction) + { + // TODO: this is temporary as well. + foreach (var c in SelectedBlueprints) + { + ((Drawable)c.Item).Scale *= new Vector2( + direction == Direction.Horizontal ? -1 : 1, + direction == Direction.Vertical ? -1 : 1 + ); + } + + return true; + } + public override bool HandleMovement(MoveSelectionEvent moveEvent) { foreach (var c in SelectedBlueprints) @@ -116,6 +131,15 @@ namespace osu.Game.Skinning.Editor // reverse the scale direction if dragging from top or left. if ((reference & Anchor.x0) > 0) scale.X = -scale.X; if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y; + + // for now aspect lock scale adjustments that occur at corners. + if (!reference.HasFlagFast(Anchor.x1) && !reference.HasFlagFast(Anchor.y1)) + { + if (reference.HasFlagFast(Anchor.x0) || reference.HasFlagFast(Anchor.x2)) + scale.Y = scale.X; + else + scale.X = scale.Y; + } } public class AnchorMenuItem : TernaryStateMenuItem From 9f8e6979dd90e23bf5b7936bb536eeb312fbb3f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 18:00:25 +0900 Subject: [PATCH 145/215] Fix display of skin blueprints when flipped --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 58fa255508..1d029c39d6 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -112,8 +112,13 @@ namespace osu.Game.Skinning.Editor drawableQuad = drawable.ScreenSpaceDrawQuad; var quad = ToLocalSpace(drawable.ScreenSpaceDrawQuad); - box.Position = quad.TopLeft; + box.Position = new Vector2( + drawable.Scale.X < 0 ? quad.TopRight.X : quad.TopLeft.X, + drawable.Scale.Y < 0 ? quad.BottomLeft.Y : quad.TopLeft.Y + ); + box.Size = quad.Size; + box.Rotation = drawable.Rotation; } From 19800f5f7f22d81bc0784796b78dc811e4a8458b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 11:24:22 +0200 Subject: [PATCH 146/215] Move `IBeatmap` arg into context --- .../Editor/Checks/CheckOffscreenObjectsTest.cs | 12 ++++++------ .../Edit/Checks/CheckOffscreenObjects.cs | 5 ++--- osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs | 5 ++--- .../Editing/Checks/CheckAudioQualityTest.cs | 14 +++++++------- .../Editing/Checks/CheckBackgroundQualityTest.cs | 14 +++++++------- .../Editing/Checks/CheckConcurrentObjectsTest.cs | 15 ++++++++------- .../Editing/Checks/CheckFilePresenceTest.cs | 12 ++++++------ .../Editing/Checks/CheckUnsnappedObjectsTest.cs | 15 ++++++++------- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 5 ++--- osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs | 8 +++++++- .../Rulesets/Edit/Checks/CheckAudioQuality.cs | 5 ++--- .../Edit/Checks/CheckBackgroundQuality.cs | 7 +++---- .../Edit/Checks/CheckConcurrentObjects.cs | 11 +++++------ .../Rulesets/Edit/Checks/CheckFilePresence.cs | 6 +++--- .../Rulesets/Edit/Checks/CheckUnsnappedObjects.cs | 7 +++---- .../Rulesets/Edit/Checks/Components/ICheck.cs | 4 +--- osu.Game/Rulesets/Edit/IBeatmapVerifier.cs | 3 +-- osu.Game/Screens/Edit/Verify/IssueList.cs | 6 +++--- 18 files changed, 76 insertions(+), 78 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 5545273af2..a6873c6de9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -225,14 +225,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOk(IBeatmap beatmap) { - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - Assert.That(check.Run(beatmap, context), Is.Empty); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + Assert.That(check.Run(context), Is.Empty); } private void assertOffscreenCircle(IBeatmap beatmap) { - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - var issues = check.Run(beatmap, context).ToList(); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); @@ -240,8 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOffscreenSlider(IBeatmap beatmap) { - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - var issues = check.Run(beatmap, context).ToList(); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 86bb7f203f..a342c2a821 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Objects; @@ -32,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks new IssueTemplateOffscreenSlider(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - foreach (var hitobject in beatmap.HitObjects) + foreach (var hitobject in context.Beatmap.HitObjects) { switch (hitobject) { diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index c62f472d75..04e881fbf3 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -17,9 +16,9 @@ namespace osu.Game.Rulesets.Osu.Edit new CheckOffscreenObjects() }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(beatmap, context)); + return checks.SelectMany(check => check.Run(context)); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs index fbd02ea54e..1cbdc43140 100644 --- a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Editing.Checks mock.SetupGet(w => w.Beatmap).Returns(beatmap); mock.SetupGet(w => w.Track).Returns((Track)null); - Assert.That(check.Run(beatmap, new BeatmapVerifierContext(mock.Object)), Is.Empty); + Assert.That(check.Run(new BeatmapVerifierContext(beatmap, mock.Object)), Is.Empty); } [Test] @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(192); - Assert.That(check.Run(beatmap, context), Is.Empty); + Assert.That(check.Run(context), Is.Empty); } [Test] @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(null); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate); @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(0); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate); @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(320); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooHighBitrate); @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(64); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run( context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooLowBitrate); @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Editing.Checks private BeatmapVerifierContext getContext(int? audioBitrate) { - return new BeatmapVerifierContext(getMockWorkingBeatmap(audioBitrate).Object); + return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(audioBitrate).Object); } /// diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs index e96ec5485d..3424cfe732 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Editing.Checks beatmap.Metadata.BackgroundFile = null; var context = getContext(null, System.Array.Empty()); - Assert.That(check.Run(beatmap, context), Is.Empty); + Assert.That(check.Run(context), Is.Empty); } [Test] @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(new Texture(1920, 1080)); - Assert.That(check.Run(beatmap, context), Is.Empty); + Assert.That(check.Run(context), Is.Empty); } [Test] @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(new Texture(3840, 2160)); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution); @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(new Texture(640, 480)); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution); @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(new Texture(100, 100)); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution); @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(new Texture(1920, 1080), new byte[1024 * 1024 * 3]); - var issues = check.Run(beatmap, context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooUncompressed); @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Editing.Checks private BeatmapVerifierContext getContext(Texture background, [CanBeNull] byte[] fileBytes = null) { - return new BeatmapVerifierContext(getMockWorkingBeatmap(background, fileBytes).Object); + return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(background, fileBytes).Object); } /// diff --git a/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs index ffe5d34e67..ba0a130a25 100644 --- a/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs @@ -6,6 +6,7 @@ using System.Linq; using Moq; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; @@ -105,7 +106,7 @@ namespace osu.Game.Tests.Editing.Checks new HitCircle { StartTime = 300 } }; - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(3)); Assert.That(issues.Where(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent).ToList(), Has.Count.EqualTo(2)); @@ -164,12 +165,12 @@ namespace osu.Game.Tests.Editing.Checks private void assertOk(List hitobjects) { - Assert.That(check.Run(getPlayableBeatmap(hitobjects), null), Is.Empty); + Assert.That(check.Run(getContext(hitobjects)), Is.Empty); } private void assertConcurrentSame(List hitobjects, int count = 1) { - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentSame)); @@ -177,18 +178,18 @@ namespace osu.Game.Tests.Editing.Checks private void assertConcurrentDifferent(List hitobjects, int count = 1) { - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent)); } - private IBeatmap getPlayableBeatmap(List hitobjects) + private BeatmapVerifierContext getContext(List hitobjects) { - return new Beatmap + return new BeatmapVerifierContext(new Beatmap { HitObjects = hitobjects - }; + }, null); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs b/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs index 424dffcbc2..39a1d76d83 100644 --- a/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs @@ -46,8 +46,8 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestBackgroundSetAndInFiles() { - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - Assert.That(check.Run(beatmap, context), Is.Empty); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + Assert.That(check.Run(context), Is.Empty); } [Test] @@ -55,8 +55,8 @@ namespace osu.Game.Tests.Editing.Checks { beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - var issues = check.Run(beatmap, context).ToList(); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist); @@ -67,8 +67,8 @@ namespace osu.Game.Tests.Editing.Checks { beatmap.Metadata.BackgroundFile = null; - var context = new BeatmapVerifierContext(new TestWorkingBeatmap(beatmap)); - var issues = check.Run(beatmap, context).ToList(); + var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet); diff --git a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs index 5e65b263f2..02159fa57b 100644 --- a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -105,7 +106,7 @@ namespace osu.Game.Tests.Editing.Checks getSliderMock(startTime: 98, endTime: 398.75d).Object }; - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(2)); Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); @@ -124,12 +125,12 @@ namespace osu.Game.Tests.Editing.Checks private void assertOk(List hitobjects) { - Assert.That(check.Run(getPlayableBeatmap(hitobjects), null), Is.Empty); + Assert.That(check.Run(getContext(hitobjects)), Is.Empty); } private void assert1Ms(List hitobjects, int count = 1) { - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); @@ -137,19 +138,19 @@ namespace osu.Game.Tests.Editing.Checks private void assert2Ms(List hitobjects, int count = 1) { - var issues = check.Run(getPlayableBeatmap(hitobjects), null).ToList(); + var issues = check.Run(getContext(hitobjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap)); } - private IBeatmap getPlayableBeatmap(List hitobjects) + private BeatmapVerifierContext getContext(List hitobjects) { - return new Beatmap + return new BeatmapVerifierContext(new Beatmap { ControlPointInfo = cpi, HitObjects = hitobjects - }; + }, null); } } } diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 1860d54b57..d208c7fe07 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; @@ -29,9 +28,9 @@ namespace osu.Game.Rulesets.Edit new CheckConcurrentObjects() }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - return checks.SelectMany(check => check.Run(beatmap, context)); + return checks.SelectMany(check => check.Run(context)); } } } diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs index 76b74cb993..53bdf3140c 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs @@ -11,6 +11,11 @@ namespace osu.Game.Rulesets.Edit /// public class BeatmapVerifierContext { + /// + /// The playable beatmap instance of the current beatmap. + /// + public readonly IBeatmap Beatmap; + /// /// The working beatmap instance of the current beatmap. /// @@ -21,8 +26,9 @@ namespace osu.Game.Rulesets.Edit /// public DifficultyRating InterpretedDifficulty; - public BeatmapVerifierContext(IWorkingBeatmap workingBeatmap, DifficultyRating difficultyRating = DifficultyRating.ExpertPlus) + public BeatmapVerifierContext(IBeatmap beatmap, IWorkingBeatmap workingBeatmap, DifficultyRating difficultyRating = DifficultyRating.ExpertPlus) { + Beatmap = beatmap; WorkingBeatmap = workingBeatmap; InterpretedDifficulty = difficultyRating; } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs index 1015f267aa..70d11883b7 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks @@ -26,9 +25,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateNoBitrate(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - var audioFile = beatmap.Metadata?.AudioFile; + var audioFile = context.Beatmap.Metadata?.AudioFile; if (audioFile == null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs index 87f5c80c89..085c558eaf 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks @@ -30,9 +29,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateTooUncompressed(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - var backgroundFile = beatmap.Metadata?.BackgroundFile; + var backgroundFile = context.Beatmap.Metadata?.BackgroundFile; if (backgroundFile == null) yield break; @@ -48,7 +47,7 @@ namespace osu.Game.Rulesets.Edit.Checks else if (texture.Width < low_width || texture.Height < low_height) yield return new IssueTemplateLowResolution(this).Create(texture.Width, texture.Height); - string storagePath = beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); + string storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(backgroundFile); double filesizeMb = context.WorkingBeatmap.GetStream(storagePath).Length / (1024d * 1024d); if (filesizeMb > max_filesize_mb) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs index fd6ed664e6..51277298ab 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -22,15 +21,15 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateConcurrentDifferent(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - for (int i = 0; i < beatmap.HitObjects.Count - 1; ++i) + for (int i = 0; i < context.Beatmap.HitObjects.Count - 1; ++i) { - var hitobject = beatmap.HitObjects[i]; + var hitobject = context.Beatmap.HitObjects[i]; - for (int j = i + 1; j < beatmap.HitObjects.Count; ++j) + for (int j = i + 1; j < context.Beatmap.HitObjects.Count; ++j) { - var nextHitobject = beatmap.HitObjects[j]; + var nextHitobject = context.Beatmap.HitObjects[j]; // Accounts for rulesets with hitobjects separated by columns, such as Mania. // In these cases we only care about concurrent objects within the same column. diff --git a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs index f04909d175..36a0bf8c5d 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs @@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateDoesNotExist(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - var filename = GetFilename(beatmap); + var filename = GetFilename(context.Beatmap); if (filename == null) { @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit.Checks } // If the file is set, also make sure it still exists. - var storagePath = beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); + var storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); if (storagePath != null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs index aa19f3df07..ded1bb54ca 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckUnsnappedObjects.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -22,11 +21,11 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateSmallUnsnap(this) }; - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context) + public IEnumerable Run(BeatmapVerifierContext context) { - var controlPointInfo = beatmap.ControlPointInfo; + var controlPointInfo = context.Beatmap.ControlPointInfo; - foreach (var hitobject in beatmap.HitObjects) + foreach (var hitobject in context.Beatmap.HitObjects) { double startUnsnap = hitobject.StartTime - controlPointInfo.GetClosestSnappedTime(hitobject.StartTime); string startPostfix = hitobject is IHasDuration ? "start" : ""; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs index 511d6aaa0f..141de55f1d 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { @@ -24,8 +23,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Runs this check and returns any issues detected for the provided beatmap. /// - /// The playable beatmap of the beatmap to run the check on. /// The beatmap verifier context associated with the beatmap. - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context); + public IEnumerable Run(BeatmapVerifierContext context); } } diff --git a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs index 1dafc6938e..06f0abedb0 100644 --- a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit @@ -12,6 +11,6 @@ namespace osu.Game.Rulesets.Edit /// public interface IBeatmapVerifier { - public IEnumerable Run(IBeatmap beatmap, BeatmapVerifierContext context); + public IEnumerable Run(BeatmapVerifierContext context); } } diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index abf22ead10..fc9d4c7526 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Verify generalVerifier = new BeatmapVerifier(); rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); - context = new BeatmapVerifierContext(workingBeatmap.Value, verify.InterpretedDifficulty.Value); + context = new BeatmapVerifierContext(beatmap, workingBeatmap.Value, verify.InterpretedDifficulty.Value); verify.InterpretedDifficulty.BindValueChanged(change => { context.InterpretedDifficulty = change.NewValue; @@ -98,10 +98,10 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { - var issues = generalVerifier.Run(beatmap, context); + var issues = generalVerifier.Run(context); if (rulesetVerifier != null) - issues = issues.Concat(rulesetVerifier.Run(beatmap, context)); + issues = issues.Concat(rulesetVerifier.Run(context)); issues = filter(issues); From df77b28b48b8a58a92380c458251efcaf73773c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 18:39:21 +0900 Subject: [PATCH 147/215] Add a flimsy guard against null parent to avoid crashes on exit sequence --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 7db5bc7ead..b337aa53c4 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -172,6 +172,9 @@ namespace osu.Game.Skinning.Editor { base.Update(); + if (drawable.Parent == null) + return; + originBox.Position = drawable.ToSpaceOfOtherDrawable(drawable.OriginPosition, this); anchorBox.Position = drawable.Parent.ToSpaceOfOtherDrawable(drawable.AnchorPosition, this); From 01bc71acd2a24c3fa5993aab6487f4fdee7d7062 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 18:40:28 +0900 Subject: [PATCH 148/215] Improve ability to parse xmldoc of `SkinnableTargetWrapper` Co-authored-by: Dan Balasescu --- osu.Game/Skinning/SkinnableTargetWrapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index 664e8da480..395de590cf 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -9,8 +9,8 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { /// - /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type (for consumption via . - /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. + /// A container which groups the elements of a into a single object. + /// Optionally also applies a default layout to the elements. /// [Serializable] public class SkinnableTargetWrapper : Container, ISkinnableDrawable From 2f025f196723c0711e1573962515cabb892daae7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 18:51:23 +0900 Subject: [PATCH 149/215] SkinnableTargetWrapper -> SkinnableTargetComponentsContainer --- osu.Game/Skinning/DefaultSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 2 +- osu.Game/Skinning/Skin.cs | 2 +- ...rapper.cs => SkinnableTargetComponentsContainer.cs} | 10 +++++----- osu.Game/Skinning/SkinnableTargetContainer.cs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) rename osu.Game/Skinning/{SkinnableTargetWrapper.cs => SkinnableTargetComponentsContainer.cs} (73%) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index d6ca5e9009..65e8fd1b82 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -47,7 +47,7 @@ namespace osu.Game.Skinning switch (target.Target) { case SkinnableTarget.MainHUDComponents: - var skinnableTargetWrapper = new SkinnableTargetWrapper(container => + var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 27f6fcdf97..a6f8f45c0f 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -332,7 +332,7 @@ namespace osu.Game.Skinning { case SkinnableTarget.MainHUDComponents: - var skinnableTargetWrapper = new SkinnableTargetWrapper(container => + var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container => { var score = container.OfType().FirstOrDefault(); var accuracy = container.OfType().FirstOrDefault(); diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 0ceca3925e..2944c7a8ec 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -87,7 +87,7 @@ namespace osu.Game.Skinning if (!DrawableComponentInfo.TryGetValue(target.Target, out var skinnableInfo)) return null; - return new SkinnableTargetWrapper + return new SkinnableTargetComponentsContainer { ChildrenEnumerable = skinnableInfo.Select(i => i.CreateInstance()) }; diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetComponentsContainer.cs similarity index 73% rename from osu.Game/Skinning/SkinnableTargetWrapper.cs rename to osu.Game/Skinning/SkinnableTargetComponentsContainer.cs index 395de590cf..2107ca7a8b 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetComponentsContainer.cs @@ -9,11 +9,11 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { /// - /// A container which groups the elements of a into a single object. - /// Optionally also applies a default layout to the elements. + /// A container which groups the components of a into a single object. + /// Optionally also applies a default layout to the components. /// [Serializable] - public class SkinnableTargetWrapper : Container, ISkinnableDrawable + public class SkinnableTargetComponentsContainer : Container, ISkinnableDrawable { public bool IsEditable => false; @@ -23,14 +23,14 @@ namespace osu.Game.Skinning /// Construct a wrapper with defaults that should be applied once. /// /// A function to apply the default layout. - public SkinnableTargetWrapper(Action applyDefaults) + public SkinnableTargetComponentsContainer(Action applyDefaults) : this() { this.applyDefaults = applyDefaults; } [JsonConstructor] - public SkinnableTargetWrapper() + public SkinnableTargetComponentsContainer() { RelativeSizeAxes = Axes.Both; } diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index 52e86f5d86..a4d7f621eb 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -10,7 +10,7 @@ namespace osu.Game.Skinning { public class SkinnableTargetContainer : SkinReloadableDrawable, ISkinnableTarget { - private SkinnableTargetWrapper content; + private SkinnableTargetComponentsContainer content; public SkinnableTarget Target { get; } @@ -31,7 +31,7 @@ namespace osu.Game.Skinning ClearInternal(); components.Clear(); - content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; + content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetComponentsContainer; if (content != null) { From 07e475cd132cde77478a45ac976173bb4abfdf67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 18:54:40 +0900 Subject: [PATCH 150/215] Fix skin blueprint box drawing incorrectly when both scale and rotation are applied --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index 1d029c39d6..b2a1b1c9d8 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.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 osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -112,14 +113,10 @@ namespace osu.Game.Skinning.Editor drawableQuad = drawable.ScreenSpaceDrawQuad; var quad = ToLocalSpace(drawable.ScreenSpaceDrawQuad); - box.Position = new Vector2( - drawable.Scale.X < 0 ? quad.TopRight.X : quad.TopLeft.X, - drawable.Scale.Y < 0 ? quad.BottomLeft.Y : quad.TopLeft.Y - ); - + box.Position = drawable.ToSpaceOfOtherDrawable(Vector2.Zero, this); box.Size = quad.Size; - box.Rotation = drawable.Rotation; + box.Scale = new Vector2(MathF.Sign(drawable.Scale.X), MathF.Sign(drawable.Scale.Y)); } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => drawable.ReceivePositionalInputAt(screenSpacePos); From e5f765d1a8ef42e9a12fe94dbe23f3d464d1972d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 19:06:58 +0900 Subject: [PATCH 151/215] Fix broken exception message --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 67285987ef..cb27a84a75 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -166,7 +166,7 @@ namespace osu.Game.Skinning.Editor private void placeComponent(Type type) { if (!(Activator.CreateInstance(type) is ISkinnableDrawable component)) - throw new InvalidOperationException("Attempted to instantiate a component for placement which was not an {typeof(ISkinnableComponent)}."); + throw new InvalidOperationException($"Attempted to instantiate a component for placement which was not an {typeof(ISkinnableDrawable)}."); getTarget(SkinnableTarget.MainHUDComponents)?.Add(component); From 94538b38429f5d7a2447c44e0617f7c868e44437 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Thu, 13 May 2021 12:56:36 +0200 Subject: [PATCH 152/215] Remove accidental whitespace --- osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs index 1cbdc43140..39fbf11d51 100644 --- a/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Editing.Checks { var context = getContext(64); - var issues = check.Run( context).ToList(); + var issues = check.Run(context).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooLowBitrate); From 1e23c535070102c326ca609f56b0d68765bcc25f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 21:59:38 +0900 Subject: [PATCH 153/215] Fix inspection --- osu.Game/Skinning/SkinnableDrawable.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 77f7cf5c3f..fc2730ca44 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -23,7 +23,7 @@ namespace osu.Game.Skinning /// Whether the drawable component should be centered in available space. /// Defaults to true. /// - public bool CentreComponent { get; set; } = true; + public bool CentreComponent = true; public new Axes AutoSizeAxes { @@ -42,7 +42,8 @@ namespace osu.Game.Skinning /// A function to create the default skin implementation of this element. /// A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present. /// How (if at all) the should be resize to fit within our own bounds. - public SkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, Func allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling) + public SkinnableDrawable(ISkinComponent component, Func defaultImplementation = null, Func allowFallback = null, + ConfineMode confineMode = ConfineMode.NoScaling) : this(component, allowFallback, confineMode) { createDefault = defaultImplementation; From 4cf4817ad2abc61de9f754b7cd9e714bd19b74e0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 May 2021 22:11:58 +0900 Subject: [PATCH 154/215] Remove redundant parens --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index f7eec4d661..79dd45c07c 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -124,7 +124,7 @@ namespace osu.Game.Skinning.Editor { var drawable = (Drawable)item; - var previousAnchor = (drawable.AnchorPosition); + var previousAnchor = drawable.AnchorPosition; drawable.Anchor = anchor; drawable.Position -= drawable.AnchorPosition - previousAnchor; } From 6c12cae105a98f0d313d16f00b2cdfa9c4960bfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 13 May 2021 22:25:11 +0900 Subject: [PATCH 155/215] Remove unnecessary property --- osu.Game/Skinning/Editor/SkinBlueprint.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinBlueprint.cs b/osu.Game/Skinning/Editor/SkinBlueprint.cs index f7509d7b8f..7a5547296b 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprint.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprint.cs @@ -23,11 +23,6 @@ namespace osu.Game.Skinning.Editor private Drawable drawable => (Drawable)Item; - /// - /// Whether the blueprint should be shown even when the is not alive. - /// - protected virtual bool AlwaysShowWhenSelected => true; - protected override bool ShouldBeAlive => drawable.IsAlive && Item.IsPresent; [Resolved] From 25b1443c5011fb7c5e3144e05159a92434206cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 13 May 2021 17:50:12 +0200 Subject: [PATCH 156/215] Remove dead branch and mark implementation as temporary The previous implementation was checking if the `x0` or `x2` anchors were selected to decide on which way to transfer the drawable's scale, but that check actually ends up being always true for corner anchors. To visualise, this is how the corner anchors correspond to `Anchor` flags: x0 x1 x2 | | | y0 -O---O---O- | | | y1 -O---+---O- | | | y2 -O---O---O- | | | The Os indicate where the reference anchors are on a selection box. The first conditional eliminates the middle ones, which makes sense. But after excluding them from further deliberations (marking via X): x0 x1 x2 | | | y0 -O---X---O- | | | y1 -X---+---X- | | | y2 -O---X---O- | | | The remaining anchors always have `x0` or `x2` set. So to avoid confusion, just always transfer one way for now. At some point this should be torn out in favour of an actual implementation of the desired behaviour. --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 485cd3fe93..9bcdc6e08b 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -158,10 +158,8 @@ namespace osu.Game.Skinning.Editor // for now aspect lock scale adjustments that occur at corners. if (!reference.HasFlagFast(Anchor.x1) && !reference.HasFlagFast(Anchor.y1)) { - if (reference.HasFlagFast(Anchor.x0) || reference.HasFlagFast(Anchor.x2)) - scale.Y = scale.X; - else - scale.X = scale.Y; + // TODO: temporary implementation - only dragging the corner handles across the X axis changes size. + scale.Y = scale.X; } } From ddceafb1b041f0360446e251f9a4755f42f114d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 09:38:25 +0900 Subject: [PATCH 157/215] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8c24df5c2e..90d131b117 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3eb5050e1a..587bdaf622 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f00e20a66e..7ba7a554d6 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 9e8c0a7e7037e6fa118f8d1b1c2c868ca25c286d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 04:25:29 +0300 Subject: [PATCH 158/215] Fix online play subscreens not pushing player loaders when starting gameplay --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 3 ++- .../Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index fa18b792c3..783b8b4bf2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -27,6 +27,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; +using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; @@ -452,7 +453,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer return new MultiSpectatorScreen(userIds); default: - return new MultiplayerPlayer(SelectedItem.Value, userIds); + return new PlayerLoader(() => new MultiplayerPlayer(SelectedItem.Value, userIds)); } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 11bc55823f..26ee21a2c3 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -13,6 +13,7 @@ using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Match; using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD; using osu.Game.Users; using osuTK; @@ -271,9 +272,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, true); } - protected override Screen CreateGameplayScreen() => new PlaylistsPlayer(SelectedItem.Value) + protected override Screen CreateGameplayScreen() => new PlayerLoader(() => new PlaylistsPlayer(SelectedItem.Value) { Exited = () => leaderboard.RefreshScores() - }; + }); } } From 90e0b3374e2a975e2df4e99cffebb520653d12c9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Fri, 14 May 2021 03:34:50 +0200 Subject: [PATCH 159/215] Add `#nullable enable` Co-authored-by: Dean Herbert --- osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs index 53bdf3140c..13a892def4 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; - + #nullable enable namespace osu.Game.Rulesets.Edit { /// From a447f200958977b86129d5159edb9891fc6e2031 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Fri, 14 May 2021 03:38:35 +0200 Subject: [PATCH 160/215] Fix formatting of `#nullable enable` --- osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs index 13a892def4..6feee82bda 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifierContext.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; - #nullable enable + +#nullable enable + namespace osu.Game.Rulesets.Edit { /// From 447371478ea6f4843b57a409ebbfab1db1d15e78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 12:03:06 +0900 Subject: [PATCH 161/215] Switch null assignment to non-nullable warnings on --- osu.sln.DotSettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index aa8f8739c1..4ac796ccd0 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -18,7 +18,7 @@ WARNING HINT DO_NOT_SHOW - HINT + WARNING WARNING WARNING WARNING From b36c991ba1ca714d9b47e34e705fba071e477e22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 12:04:38 +0900 Subject: [PATCH 162/215] Fix single case of incorrect usage --- osu.Game/Screens/Edit/Editor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 434683a016..5ac3401720 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -635,6 +635,9 @@ namespace osu.Game.Screens.Edit case EditorScreenMode.Verify: currentScreen = new VerifyScreen(); break; + + default: + throw new InvalidOperationException("Editor menu bar switched to an unsupported mode"); } LoadComponentAsync(currentScreen, newScreen => From 46e7d9e0edbc18a48e93db72e616a7af67482c75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 12:15:56 +0900 Subject: [PATCH 163/215] Randomise the values displayed in the skinning toolbox To stop the spam of "WYSI" comments everywhere. I guess I underestimated the negative effect this would have. --- osu.Game/Skinning/Editor/SkinComponentToolbox.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs index 8536cba139..59420bfc87 100644 --- a/osu.Game/Skinning/Editor/SkinComponentToolbox.cs +++ b/osu.Game/Skinning/Editor/SkinComponentToolbox.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -29,8 +30,8 @@ namespace osu.Game.Skinning.Editor [Cached] private ScoreProcessor scoreProcessor = new ScoreProcessor { - Combo = { Value = 727 }, - TotalScore = { Value = 1337377 } + Combo = { Value = RNG.Next(1, 1000) }, + TotalScore = { Value = RNG.Next(1000, 10000000) } }; [Cached(typeof(HealthProcessor))] From 9860e482afe591d5b68916fe594b77ff913ef85e Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Fri, 14 May 2021 05:32:52 +0200 Subject: [PATCH 164/215] Use `TestWorkingBeatmap` instead of null in tests Fixes the warning that #12801 will give. --- .../Editing/Checks/CheckConcurrentObjectsTest.cs | 7 +++---- osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs | 7 +++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs index ba0a130a25..5adb91a22e 100644 --- a/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Editing.Checks { @@ -186,10 +187,8 @@ namespace osu.Game.Tests.Editing.Checks private BeatmapVerifierContext getContext(List hitobjects) { - return new BeatmapVerifierContext(new Beatmap - { - HitObjects = hitobjects - }, null); + var beatmap = new Beatmap { HitObjects = hitobjects }; + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs index 02159fa57b..639f5b5ab4 100644 --- a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Editing.Checks { @@ -146,11 +147,13 @@ namespace osu.Game.Tests.Editing.Checks private BeatmapVerifierContext getContext(List hitobjects) { - return new BeatmapVerifierContext(new Beatmap + var beatmap = new Beatmap { ControlPointInfo = cpi, HitObjects = hitobjects - }, null); + }; + + return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); } } } From 48672f8afd156556f29628b3ae246013f72c297d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 15:02:36 +0900 Subject: [PATCH 165/215] Add very basic test logic to ensure `PlayerLoader` is present for playlists --- .../TestScenePlaylistsRoomSubScreen.cs | 20 +++++++++++++++++-- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 6 +++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs index 319c2bc6fd..264004b6c3 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs @@ -11,11 +11,13 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Playlists; +using osu.Game.Screens.Play; using osu.Game.Tests.Beatmaps; using osu.Game.Users; using osuTK.Input; @@ -24,8 +26,6 @@ namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsRoomSubScreen : RoomTestScene { - protected override bool UseOnlineAPI => true; - [Cached(typeof(IRoomManager))] private readonly TestRoomManager roomManager = new TestRoomManager(); @@ -41,6 +41,18 @@ namespace osu.Game.Tests.Visual.Playlists Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); + + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case CreateRoomScoreRequest createRoomScoreRequest: + createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 }); + return true; + } + + return false; + }; } [SetUpSteps] @@ -59,12 +71,16 @@ namespace osu.Game.Tests.Visual.Playlists Room.Name.Value = "my awesome room"; Room.Host.Value = new User { Id = 2, Username = "peppy" }; Room.RecentParticipants.Add(Room.Host.Value); + Room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); Room.Playlist.Add(new PlaylistItem { Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); }); + + AddStep("start match", () => match.ChildrenOfType().First().Click()); + AddUntilStep("player loader loaded", () => Stack.CurrentScreen is PlayerLoader); } [Test] diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 68bdd9160e..28156aecf3 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -150,7 +150,11 @@ namespace osu.Game.Screens.OnlinePlay.Match protected void StartPlay() { sampleStart?.Play(); - ParentScreen?.Push(CreateGameplayScreen()); + + // fallback is to allow this class to operate when there is no parent OnlineScreen (testing purposes). + var targetScreen = (Screen)ParentScreen ?? this; + + targetScreen?.Push(CreateGameplayScreen()); } /// From 8338f702c3b260dbd01e8dac1b4c19ce01364fb6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 09:32:56 +0300 Subject: [PATCH 166/215] Remove not required null conditional --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 28156aecf3..375aac729d 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.OnlinePlay.Match // fallback is to allow this class to operate when there is no parent OnlineScreen (testing purposes). var targetScreen = (Screen)ParentScreen ?? this; - targetScreen?.Push(CreateGameplayScreen()); + targetScreen.Push(CreateGameplayScreen()); } /// From 32ff40628973ff325c213cd51a32f2324b722df6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 15:40:29 +0900 Subject: [PATCH 167/215] Add database tracking of beatmap creator `user_id`s --- ...BeatmapManager_BeatmapOnlineLookupQueue.cs | 26 +- osu.Game/Beatmaps/BeatmapMetadata.cs | 21 +- ...9_AddAuthorIdToBeatmapMetadata.Designer.cs | 511 ++++++++++++++++++ ...0514062639_AddAuthorIdToBeatmapMetadata.cs | 23 + .../Migrations/OsuDbContextModelSnapshot.cs | 3 + 5 files changed, 570 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs create mode 100644 osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index 7c4b344c9e..5dff4fe282 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading; @@ -83,6 +82,12 @@ namespace osu.Game.Beatmaps beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + if (beatmap.Metadata != null) + beatmap.Metadata.AuthorID = res.AuthorID; + + if (beatmap.BeatmapSet.Metadata != null) + beatmap.BeatmapSet.Metadata.AuthorID = res.AuthorID; + LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}."); } } @@ -157,7 +162,7 @@ namespace osu.Game.Beatmaps using (var cmd = db.CreateCommand()) { - cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; + cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineBeatmapID OR filename = @Path"; cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmap.MD5Hash)); cmd.Parameters.Add(new SqliteParameter("@OnlineBeatmapID", beatmap.OnlineBeatmapID ?? (object)DBNull.Value)); @@ -174,6 +179,12 @@ namespace osu.Game.Beatmaps beatmap.BeatmapSet.OnlineBeatmapSetID = reader.GetInt32(0); beatmap.OnlineBeatmapID = reader.GetInt32(1); + if (beatmap.Metadata != null) + beatmap.Metadata.AuthorID = reader.GetInt32(3); + + if (beatmap.BeatmapSet.Metadata != null) + beatmap.BeatmapSet.Metadata.AuthorID = reader.GetInt32(3); + LogForModel(set, $"Cached local retrieval for {beatmap}."); return true; } @@ -194,17 +205,6 @@ namespace osu.Game.Beatmaps cacheDownloadRequest?.Dispose(); updateScheduler?.Dispose(); } - - [Serializable] - [SuppressMessage("ReSharper", "InconsistentNaming")] - private class CachedOnlineBeatmapLookup - { - public int approved { get; set; } - - public int? beatmapset_id { get; set; } - - public int? beatmap_id { get; set; } - } } } } diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 858da8e602..9540a216fc 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -34,6 +34,21 @@ namespace osu.Game.Beatmaps [JsonIgnore] public List BeatmapSets { get; set; } + /// + /// Helper property to deserialize a username to . + /// + [JsonProperty(@"user_id")] + [Column("AuthorID")] + public int AuthorID + { + get => Author?.Id ?? 1; + set + { + Author ??= new User(); + Author.Id = value; + } + } + /// /// Helper property to deserialize a username to . /// @@ -42,7 +57,11 @@ namespace osu.Game.Beatmaps public string AuthorString { get => Author?.Username; - set => Author = new User { Username = value }; + set + { + Author ??= new User(); + Author.Username = value; + } } /// diff --git a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs new file mode 100644 index 0000000000..89bab3a0fa --- /dev/null +++ b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.Designer.cs @@ -0,0 +1,511 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210514062639_AddAuthorIdToBeatmapMetadata")] + partial class AddAuthorIdToBeatmapMetadata + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("EpilepsyWarning"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorID") + .HasColumnName("AuthorID"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs new file mode 100644 index 0000000000..98fe9b5e13 --- /dev/null +++ b/osu.Game/Migrations/20210514062639_AddAuthorIdToBeatmapMetadata.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddAuthorIdToBeatmapMetadata : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AuthorID", + table: "BeatmapMetadata", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AuthorID", + table: "BeatmapMetadata"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index d4bde50b60..f518cfb42b 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -126,6 +126,9 @@ namespace osu.Game.Migrations b.Property("AudioFile"); + b.Property("AuthorID") + .HasColumnName("AuthorID"); + b.Property("AuthorString") .HasColumnName("Author"); From d09da02673186d308e95da4482a06dade52a2a2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 16:03:22 +0900 Subject: [PATCH 168/215] Fix deleting skin elements not saving out to skin Closes https://github.com/ppy/osu/issues/12786. --- osu.Game/Skinning/Editor/SkinEditor.cs | 15 +++++++++++--- .../Skinning/Editor/SkinSelectionHandler.cs | 14 ++++++------- osu.Game/Skinning/ISkinnableTarget.cs | 9 ++++++++- osu.Game/Skinning/SkinnableTargetContainer.cs | 20 +++++++++++++++---- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index 6427d6298b..c594f8d271 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -193,14 +194,16 @@ namespace osu.Game.Skinning.Editor SelectedComponents.Add(component); } + private IEnumerable availableTargets => targetScreen.ChildrenOfType(); + private ISkinnableTarget getTarget(SkinnableTarget target) { - return targetScreen.ChildrenOfType().FirstOrDefault(c => c.Target == target); + return availableTargets.FirstOrDefault(c => c.Target == target); } private void revert() { - SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); + ISkinnableTarget[] targetContainers = availableTargets.ToArray(); foreach (var t in targetContainers) { @@ -216,7 +219,7 @@ namespace osu.Game.Skinning.Editor if (!hasBegunMutating) return; - SkinnableTargetContainer[] targetContainers = targetScreen.ChildrenOfType().ToArray(); + ISkinnableTarget[] targetContainers = availableTargets.ToArray(); foreach (var t in targetContainers) currentSkin.Value.UpdateDrawableTarget(t); @@ -237,5 +240,11 @@ namespace osu.Game.Skinning.Editor { this.FadeOut(TRANSITION_DURATION, Easing.OutQuint); } + + public void DeleteItems(ISkinnableDrawable[] items) + { + foreach (var item in items.ToArray()) + availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item); + } } } diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 9bcdc6e08b..7931a5ec41 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; @@ -17,6 +18,9 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { + [Resolved] + private SkinEditor skinEditor { get; set; } + public override bool HandleRotation(float angle) { // TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. @@ -72,14 +76,8 @@ namespace osu.Game.Skinning.Editor SelectionBox.CanReverse = false; } - protected override void DeleteItems(IEnumerable items) - { - foreach (var i in items) - { - ((Drawable)i).Expire(); - SelectedItems.Remove(i); - } - } + protected override void DeleteItems(IEnumerable items) => + skinEditor.DeleteItems(items.ToArray()); protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable> selection) { diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 65b8ba7b17..15cad2c817 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -37,8 +37,15 @@ namespace osu.Game.Skinning void Reload(); /// - /// Add the provided item to this target. + /// Add a new skinnable component to this target. /// + /// The component to add. void Add(ISkinnableDrawable drawable); + + /// + /// Remove an existing skinnable component to this target. + /// + /// The component to add. + public void Remove(ISkinnableDrawable component); } } diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index a4d7f621eb..f055fb2533 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -43,10 +43,7 @@ namespace osu.Game.Skinning } } - /// - /// Add a new skinnable component to this target. - /// - /// The component to add. + /// /// Thrown when attempting to add an element to a target which is not supported by the current skin. /// Thrown if the provided instance is not a . public void Add(ISkinnableDrawable component) @@ -61,6 +58,21 @@ namespace osu.Game.Skinning components.Add(component); } + /// + /// Thrown when attempting to add an element to a target which is not supported by the current skin. + /// Thrown if the provided instance is not a . + public void Remove(ISkinnableDrawable component) + { + if (content == null) + throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); + + if (!(component is Drawable drawable)) + throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable)); + + content.Remove(drawable); + components.Remove(component); + } + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); From 9069db07437154554fb6c1b55fa323dcac2c6c45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 16:22:19 +0900 Subject: [PATCH 169/215] Fix case of `hitObjects` variables --- .../Checks/CheckUnsnappedObjectsTest.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs index 639f5b5ab4..882baba8fa 100644 --- a/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs @@ -102,12 +102,12 @@ namespace osu.Game.Tests.Editing.Checks }, count: 2); // Start and end are 2 ms and 1.25 ms off respectively, hence two different issues in one object. - var hitobjects = new List + var hitObjects = new List { getSliderMock(startTime: 98, endTime: 398.75d).Object }; - var issues = check.Run(getContext(hitobjects)).ToList(); + var issues = check.Run(getContext(hitObjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(2)); Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); @@ -124,33 +124,33 @@ namespace osu.Game.Tests.Editing.Checks return mockSlider; } - private void assertOk(List hitobjects) + private void assertOk(List hitObjects) { - Assert.That(check.Run(getContext(hitobjects)), Is.Empty); + Assert.That(check.Run(getContext(hitObjects)), Is.Empty); } - private void assert1Ms(List hitobjects, int count = 1) + private void assert1Ms(List hitObjects, int count = 1) { - var issues = check.Run(getContext(hitobjects)).ToList(); + var issues = check.Run(getContext(hitObjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap)); } - private void assert2Ms(List hitobjects, int count = 1) + private void assert2Ms(List hitObjects, int count = 1) { - var issues = check.Run(getContext(hitobjects)).ToList(); + var issues = check.Run(getContext(hitObjects)).ToList(); Assert.That(issues, Has.Count.EqualTo(count)); Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap)); } - private BeatmapVerifierContext getContext(List hitobjects) + private BeatmapVerifierContext getContext(List hitObjects) { var beatmap = new Beatmap { ControlPointInfo = cpi, - HitObjects = hitobjects + HitObjects = hitObjects }; return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap)); From fcb226bd206ed17e01de4aa18e0ed2ece8416f80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 16:23:45 +0900 Subject: [PATCH 170/215] Add local variable for regular access to `HitObjects` --- .../Rulesets/Edit/Checks/CheckConcurrentObjects.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs index 51277298ab..ba5fbcf58d 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs @@ -23,13 +23,15 @@ namespace osu.Game.Rulesets.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { - for (int i = 0; i < context.Beatmap.HitObjects.Count - 1; ++i) - { - var hitobject = context.Beatmap.HitObjects[i]; + var hitObjects = context.Beatmap.HitObjects; - for (int j = i + 1; j < context.Beatmap.HitObjects.Count; ++j) + for (int i = 0; i < hitObjects.Count - 1; ++i) + { + var hitobject = hitObjects[i]; + + for (int j = i + 1; j < hitObjects.Count; ++j) { - var nextHitobject = context.Beatmap.HitObjects[j]; + var nextHitobject = hitObjects[j]; // Accounts for rulesets with hitobjects separated by columns, such as Mania. // In these cases we only care about concurrent objects within the same column. From 67a99c83a35ab4e4cf5281faddce1b4b689f1f42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 14 May 2021 16:24:52 +0900 Subject: [PATCH 171/215] Tidy bindable changed code up --- osu.Game/Screens/Edit/Verify/IssueList.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueList.cs b/osu.Game/Screens/Edit/Verify/IssueList.cs index fc9d4c7526..0b1f988447 100644 --- a/osu.Game/Screens/Edit/Verify/IssueList.cs +++ b/osu.Game/Screens/Edit/Verify/IssueList.cs @@ -46,10 +46,7 @@ namespace osu.Game.Screens.Edit.Verify rulesetVerifier = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); context = new BeatmapVerifierContext(beatmap, workingBeatmap.Value, verify.InterpretedDifficulty.Value); - verify.InterpretedDifficulty.BindValueChanged(change => - { - context.InterpretedDifficulty = change.NewValue; - }); + verify.InterpretedDifficulty.BindValueChanged(difficulty => context.InterpretedDifficulty = difficulty.NewValue); RelativeSizeAxes = Axes.Both; From 0655825057cf47520211f5ac99021eb2730cf5e2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 14:24:56 +0300 Subject: [PATCH 172/215] Separate changing star rating display to own test --- .../Ranking/TestSceneStarRatingDisplay.cs | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index 2ff664a0d9..64f78b0802 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Screens.Ranking.Expanded; +using osuTK; namespace osu.Game.Tests.Visual.Ranking { @@ -15,28 +16,38 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestDisplay() { - StarRatingDisplay changingStarRating = null; - AddStep("load displays", () => Child = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, Children = new Drawable[] { - new StarRatingDisplay(new StarDifficulty(1.23, 0)), - new StarRatingDisplay(new StarDifficulty(2.34, 0)), - new StarRatingDisplay(new StarDifficulty(3.45, 0)), - new StarRatingDisplay(new StarDifficulty(4.56, 0)), - new StarRatingDisplay(new StarDifficulty(5.67, 0)), - new StarRatingDisplay(new StarDifficulty(6.78, 0)), - new StarRatingDisplay(new StarDifficulty(10.11, 0)), - changingStarRating = new StarRatingDisplay(), + new StarRatingDisplay(new StarDifficulty(1.23, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(2.34, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(3.45, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(4.56, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(5.67, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(6.78, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, + new StarRatingDisplay(new StarDifficulty(10.11, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, } }); + } - AddRepeatStep("change bottom rating", () => + [Test] + public void TestChangingStarRatingDisplay() + { + StarRatingDisplay starRating = null; + + AddStep("load display", () => Child = starRating = new StarRatingDisplay { - changingStarRating.Current.Value = new StarDifficulty(RNG.NextDouble(0, 10), RNG.Next()); + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(3f), + }); + + AddRepeatStep("change display value", () => + { + starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), RNG.Next(2000)); }, 10); } } From 34aab11ff3fc9e3f9c0370ff076bfd00e25d3cb8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 14:25:46 +0300 Subject: [PATCH 173/215] Add null star rating display test case --- .../Visual/Ranking/TestSceneStarRatingDisplay.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index 64f78b0802..7730d6868d 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -33,6 +33,17 @@ namespace osu.Game.Tests.Visual.Ranking }); } + [Test] + public void TestNullStarRatingDisplay() + { + AddStep("load null", () => Child = new StarRatingDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(3f), + }); + } + [Test] public void TestChangingStarRatingDisplay() { From e0728a6e194297b55eb8f9f4502f919ba3907a0d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 15:52:36 +0300 Subject: [PATCH 174/215] Make `BeatmapDifficultyCache.GetDifficultyAsync` virtual --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 53d82c385d..6ed623d0c0 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -103,8 +103,8 @@ namespace osu.Game.Beatmaps /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, - CancellationToken cancellationToken = default) + public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; From db361efecfacce01093783caa0031bee658cdd02 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 15:52:51 +0300 Subject: [PATCH 175/215] Add test beatmap difficulty cache with calc. blocking support --- .../TestSceneBeatmapMetadataDisplay.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index 230822e070..271fbde5c3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -4,12 +4,15 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; @@ -24,6 +27,9 @@ namespace osu.Game.Tests.Visual.SongSelect [Resolved] private BeatmapManager manager { get; set; } + [Cached(typeof(BeatmapDifficultyCache))] + private readonly TestBeatmapDifficultyCache testDifficultyCache = new TestBeatmapDifficultyCache(); + [Test] public void TestLocal([Values("Beatmap", "Some long title and stuff")] string title, @@ -44,6 +50,27 @@ namespace osu.Game.Tests.Visual.SongSelect })); } + [Test] + public void TestDelayedStarRating() + { + AddStep("block calculation", () => testDifficultyCache.BlockCalculation = true); + + showMetadataForBeatmap(() => CreateWorkingBeatmap(new Beatmap + { + BeatmapInfo = + { + Metadata = new BeatmapMetadata + { + Title = "Heavy beatmap", + }, + Version = "10k objects", + StarDifficulty = 99.99f, + } + })); + + AddStep("allow calculation", () => testDifficultyCache.BlockCalculation = false); + } + [Test] public void TestRandomFromDatabase() { @@ -68,8 +95,11 @@ namespace osu.Game.Tests.Visual.SongSelect OsuLogo logo = new OsuLogo { Scale = new Vector2(0.15f) }; + Remove(testDifficultyCache); + Children = new Drawable[] { + testDifficultyCache, display = new BeatmapMetadataDisplay(getBeatmap(), new Bindable>(randomMods), logo) { Anchor = Anchor.Centre, @@ -85,5 +115,37 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("finish loading", () => display.Loading = false); } + + private class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + private TaskCompletionSource calculationBlocker; + + private bool blockCalculation; + + public bool BlockCalculation + { + get => blockCalculation; + set + { + if (value == blockCalculation) + return; + + blockCalculation = value; + + if (value) + calculationBlocker = new TaskCompletionSource(); + else + calculationBlocker?.SetResult(false); + } + } + + public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + { + if (blockCalculation) + await calculationBlocker.Task; + + return await base.GetDifficultyAsync(beatmapInfo, rulesetInfo, mods, cancellationToken); + } + } } } From 0dc3bfd0c11cc45f060ecb627cef78db1beb6f5c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 15:55:26 +0300 Subject: [PATCH 176/215] Apply simple transforms to star rating display when ready if not --- .../Screens/Play/BeatmapMetadataDisplay.cs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 0164fe9179..6dd9900d8c 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -53,11 +53,12 @@ namespace osu.Game.Screens.Play private IBindable starDifficulty; + private FillFlowContainer versionFlow; + private StarRatingDisplay starRatingDisplay; + [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache difficultyCache) { - StarRatingDisplay starRatingDisplay; - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; @@ -112,7 +113,7 @@ namespace osu.Game.Screens.Play loading = new LoadingLayer(true) } }, - new FillFlowContainer + versionFlow = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.TopCentre, @@ -178,11 +179,32 @@ namespace osu.Game.Screens.Play }; starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo); - starDifficulty.BindValueChanged(d => starRatingDisplay.Current.Value = d.NewValue, true); Loading = true; } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (starDifficulty.Value != null) + starRatingDisplay.Current.Value = starDifficulty.Value; + else + { + starRatingDisplay.Hide(); + + starDifficulty.ValueChanged += d => + { + starRatingDisplay.Current.Value = d.NewValue; + + versionFlow.AutoSizeDuration = 300; + versionFlow.AutoSizeEasing = Easing.OutQuint; + + starRatingDisplay.FadeIn(300, Easing.InQuint); + }; + } + } + private class MetadataLineLabel : OsuSpriteText { public MetadataLineLabel(string text) From 6cc678f497b1cdb1aa461ebd4749e145fdc5c80f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 16:01:25 +0300 Subject: [PATCH 177/215] Remove nullability and transition support from star rating display --- .../Ranking/TestSceneStarRatingDisplay.cs | 23 +++++-------- .../Ranking/Expanded/StarRatingDisplay.cs | 33 +++++-------------- 2 files changed, 17 insertions(+), 39 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index 7730d6868d..ef077970d6 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs @@ -33,33 +33,28 @@ namespace osu.Game.Tests.Visual.Ranking }); } - [Test] - public void TestNullStarRatingDisplay() - { - AddStep("load null", () => Child = new StarRatingDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(3f), - }); - } - [Test] public void TestChangingStarRatingDisplay() { StarRatingDisplay starRating = null; - AddStep("load display", () => Child = starRating = new StarRatingDisplay + AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(3f), }); - AddRepeatStep("change display value", () => + AddRepeatStep("set random value", () => { - starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), RNG.Next(2000)); + starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1); }, 10); + + AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d => + { + if (starRating != null) + starRating.Current.Value = new StarDifficulty(d, 1); + }); } } } diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 422833555f..625bdaf888 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Ranking.Expanded /// /// A pill that displays the star rating of a . /// - public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue + public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue { private Box background; private OsuTextFlowContainer textFlow; @@ -30,21 +30,14 @@ namespace osu.Game.Screens.Ranking.Expanded [Resolved] private OsuColour colours { get; set; } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + private readonly BindableWithCurrent current = new BindableWithCurrent(); - public Bindable Current + public Bindable Current { get => current.Current; set => current.Current = value; } - /// - /// Creates a new without any set, displaying a placeholder until is changed. - /// - public StarRatingDisplay() - { - } - /// /// Creates a new using an already computed . /// @@ -112,26 +105,16 @@ namespace osu.Game.Screens.Ranking.Expanded private void updateDisplay() { - const double duration = 400; - const Easing easing = Easing.OutQuint; - - double stars = Current.Value?.Stars ?? 0.00f; - - var starRatingParts = stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); + var starRatingParts = Current.Value.Stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.'); string wholePart = starRatingParts[0]; string fractionPart = starRatingParts[1]; string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - if (Current.Value == null) - background.FadeColour(Color4.SlateGray.Opacity(0.3f)); - else - { - var rating = Current.Value.Value.DifficultyRating; + var rating = Current.Value.DifficultyRating; - background.FadeColour(rating == DifficultyRating.ExpertPlus - ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) - : (ColourInfo)colours.ForDifficultyRating(rating), duration, easing); - } + background.Colour = rating == DifficultyRating.ExpertPlus + ? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959")) + : (ColourInfo)colours.ForDifficultyRating(rating); textFlow.Clear(); From 7c2fc9b412e5e5835ea0b949de5814224d201de7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 14 May 2021 16:12:25 +0300 Subject: [PATCH 178/215] Update usage due to nullability removal --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 6dd9900d8c..e26dd24de7 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -130,8 +131,9 @@ namespace osu.Game.Screens.Play Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - starRatingDisplay = new StarRatingDisplay + starRatingDisplay = new StarRatingDisplay(default) { + Alpha = 0f, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, } @@ -188,14 +190,19 @@ namespace osu.Game.Screens.Play base.LoadComplete(); if (starDifficulty.Value != null) - starRatingDisplay.Current.Value = starDifficulty.Value; + { + starRatingDisplay.Current.Value = starDifficulty.Value.Value; + starRatingDisplay.Show(); + } else { starRatingDisplay.Hide(); starDifficulty.ValueChanged += d => { - starRatingDisplay.Current.Value = d.NewValue; + Debug.Assert(d.NewValue != null); + + starRatingDisplay.Current.Value = d.NewValue.Value; versionFlow.AutoSizeDuration = 300; versionFlow.AutoSizeEasing = Easing.OutQuint; From 67dfeeb1b77eaa009aff89aa666fdf3ad12ec365 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 14 May 2021 21:23:56 +0800 Subject: [PATCH 179/215] Cleanup code in ModHidden --- .../Mods/CatchModHidden.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 14 ++++---- osu.Game/Rulesets/Mods/ModHidden.cs | 35 ------------------- 3 files changed, 8 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index bba42dea97..ba3e8f9a8f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Catch.Mods var offset = hitObject.TimePreempt * fade_out_offset_multiplier; var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier; - using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset, true)) + using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset)) drawable.FadeOut(duration); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 45f314af7b..b0f3f49939 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -65,13 +65,13 @@ namespace osu.Game.Rulesets.Osu.Mods switch (drawableObject) { case DrawableSliderTail _: - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) drawableObject.FadeOut(fadeOut.duration); break; case DrawableSliderRepeat sliderRepeat: - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) // only apply to circle piece – reverse arrow is not affected by hidden. sliderRepeat.CirclePiece.FadeOut(fadeOut.duration); @@ -88,22 +88,22 @@ namespace osu.Game.Rulesets.Osu.Mods else { // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt, true)) + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) circle.ApproachCircle.Hide(); } - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) fadeTarget.FadeOut(fadeOut.duration); break; case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (slider.BeginAbsoluteSequence(fadeOut.startTime)) slider.Body.FadeOut(fadeOut.duration, Easing.Out); break; case DrawableSliderTick sliderTick: - using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime)) sliderTick.FadeOut(fadeOut.duration); break; @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Mods // hide elements we don't care about. // todo: hide background - using (spinner.BeginAbsoluteSequence(fadeOut.startTime, true)) + using (spinner.BeginAbsoluteSequence(fadeOut.startTime)) spinner.FadeOut(fadeOut.duration); break; diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index df421adbe5..03932baca9 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.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.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics.Sprites; @@ -18,14 +17,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override bool Ranked => true; - /// - /// Check whether the provided hitobject should be considered the "first" hideable object. - /// Can be used to skip spinners, for instance. - /// - /// The hitobject to check. - [Obsolete("Use IsFirstAdjustableObject() instead.")] // Can be removed 20210506 - protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true; - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+ @@ -49,36 +40,10 @@ namespace osu.Game.Rulesets.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { -#pragma warning disable 618 - ApplyFirstObjectIncreaseVisibilityState(hitObject, state); -#pragma warning restore 618 } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { -#pragma warning disable 618 - ApplyHiddenState(hitObject, state); -#pragma warning restore 618 - } - - /// - /// Apply a special visibility state to the first object in a beatmap, if the user chooses to turn on the "increase first object visibility" setting. - /// - /// The hit object to apply the state change to. - /// The state of the hit object. - [Obsolete("Use ApplyIncreasedVisibilityState() instead.")] // Can be removed 20210506 - protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } - - /// - /// Apply a hidden state to the provided object. - /// - /// The hit object to apply the state change to. - /// The state of the hit object. - [Obsolete("Use ApplyNormalVisibilityState() instead.")] // Can be removed 20210506 - protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) - { } } } From 393ac4fdd1d6ab2ea08b0658f9d907696f1937d1 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 14 May 2021 21:30:58 +0800 Subject: [PATCH 180/215] Destruct declaration --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index b0f3f49939..4f1e91974a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -60,20 +60,20 @@ namespace osu.Game.Rulesets.Osu.Mods OsuHitObject hitObject = drawableOsuObject.HitObject; - (double startTime, double duration) fadeOut = getFadeOutParameters(drawableOsuObject); + (double startTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); switch (drawableObject) { case DrawableSliderTail _: - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) - drawableObject.FadeOut(fadeOut.duration); + using (drawableObject.BeginAbsoluteSequence(startTime)) + drawableObject.FadeOut(fadeDuration); break; case DrawableSliderRepeat sliderRepeat: - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) + using (drawableObject.BeginAbsoluteSequence(startTime)) // only apply to circle piece – reverse arrow is not affected by hidden. - sliderRepeat.CirclePiece.FadeOut(fadeOut.duration); + sliderRepeat.CirclePiece.FadeOut(fadeDuration); break; @@ -92,19 +92,19 @@ namespace osu.Game.Rulesets.Osu.Mods circle.ApproachCircle.Hide(); } - using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime)) - fadeTarget.FadeOut(fadeOut.duration); + using (drawableObject.BeginAbsoluteSequence(startTime)) + fadeTarget.FadeOut(fadeDuration); break; case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeOut.startTime)) - slider.Body.FadeOut(fadeOut.duration, Easing.Out); + using (slider.BeginAbsoluteSequence(startTime)) + slider.Body.FadeOut(fadeDuration, Easing.Out); break; case DrawableSliderTick sliderTick: - using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime)) - sliderTick.FadeOut(fadeOut.duration); + using (sliderTick.BeginAbsoluteSequence(startTime)) + sliderTick.FadeOut(fadeDuration); break; @@ -112,14 +112,14 @@ namespace osu.Game.Rulesets.Osu.Mods // hide elements we don't care about. // todo: hide background - using (spinner.BeginAbsoluteSequence(fadeOut.startTime)) - spinner.FadeOut(fadeOut.duration); + using (spinner.BeginAbsoluteSequence(startTime)) + spinner.FadeOut(fadeDuration); break; } } - private (double startTime, double duration) getFadeOutParameters(DrawableOsuHitObject drawableObject) + private (double startTime, double fadeDuration) getFadeOutParameters(DrawableOsuHitObject drawableObject) { switch (drawableObject) { @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Mods return getParameters(drawableObject.HitObject); } - static (double startTime, double duration) getParameters(OsuHitObject hitObject) + static (double startTime, double fadeDuration) getParameters(OsuHitObject hitObject) { var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; From a86a4bab9137ef6781404dc116101662c4c6d858 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 14 May 2021 21:53:59 +0800 Subject: [PATCH 181/215] Remove empty override --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 -- osu.Game/Rulesets/Mods/ModHidden.cs | 9 --------- 2 files changed, 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 4f1e91974a..c2ddeb5fcc 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -43,13 +43,11 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyIncreasedVisibilityState(hitObject, state); applyState(hitObject, true); } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); applyState(hitObject, false); } diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 03932baca9..238612b3d2 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -37,13 +36,5 @@ namespace osu.Game.Rulesets.Mods return rank; } } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } } } From a4c1b9a1a7195dca0f4b5f5f078774f306c0c916 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Fri, 14 May 2021 21:56:13 +0800 Subject: [PATCH 182/215] Rename startTime to fadeStartTime --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index c2ddeb5fcc..4168293749 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -58,18 +58,18 @@ namespace osu.Game.Rulesets.Osu.Mods OsuHitObject hitObject = drawableOsuObject.HitObject; - (double startTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); + (double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); switch (drawableObject) { case DrawableSliderTail _: - using (drawableObject.BeginAbsoluteSequence(startTime)) + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) drawableObject.FadeOut(fadeDuration); break; case DrawableSliderRepeat sliderRepeat: - using (drawableObject.BeginAbsoluteSequence(startTime)) + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) // only apply to circle piece – reverse arrow is not affected by hidden. sliderRepeat.CirclePiece.FadeOut(fadeDuration); @@ -90,18 +90,18 @@ namespace osu.Game.Rulesets.Osu.Mods circle.ApproachCircle.Hide(); } - using (drawableObject.BeginAbsoluteSequence(startTime)) + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) fadeTarget.FadeOut(fadeDuration); break; case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(startTime)) + using (slider.BeginAbsoluteSequence(fadeStartTime)) slider.Body.FadeOut(fadeDuration, Easing.Out); break; case DrawableSliderTick sliderTick: - using (sliderTick.BeginAbsoluteSequence(startTime)) + using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) sliderTick.FadeOut(fadeDuration); break; @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Mods // hide elements we don't care about. // todo: hide background - using (spinner.BeginAbsoluteSequence(startTime)) + using (spinner.BeginAbsoluteSequence(fadeStartTime)) spinner.FadeOut(fadeDuration); break; From 8b4e6d29112db730e873dd5828bcb3cd6cda514c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 21:52:09 +0200 Subject: [PATCH 183/215] Remove no longer necessary `FinishTransforms(true)` call As the component no longer has any transition transforms applied. --- osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs index 625bdaf888..7aba699216 100644 --- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs +++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs @@ -100,7 +100,6 @@ namespace osu.Game.Screens.Ranking.Expanded base.LoadComplete(); Current.BindValueChanged(_ => updateDisplay(), true); - FinishTransforms(true); } private void updateDisplay() From 1c92b3a8f52af41f45d026c41a0d09ce890a7b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 21:58:04 +0200 Subject: [PATCH 184/215] De-duplicate star rating display creation in test --- .../Ranking/TestSceneStarRatingDisplay.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs index ef077970d6..566452249f 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.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.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -20,16 +21,20 @@ namespace osu.Game.Tests.Visual.Ranking { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new Drawable[] + ChildrenEnumerable = new[] { - new StarRatingDisplay(new StarDifficulty(1.23, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(2.34, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(3.45, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(4.56, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(5.67, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(6.78, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - new StarRatingDisplay(new StarDifficulty(10.11, 0)) { Anchor = Anchor.Centre, Origin = Anchor.Centre }, - } + 1.23, + 2.34, + 3.45, + 4.56, + 5.67, + 6.78, + 10.11, + }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }) }); } From 876f53bf3beb7c2f212b1634579326a5b2c7c6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 22:15:43 +0200 Subject: [PATCH 185/215] Fix copy-paste oversights in xmldoc & exception messages --- osu.Game/Skinning/ISkinnableTarget.cs | 4 ++-- osu.Game/Skinning/SkinnableTargetContainer.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 15cad2c817..8d4f4dd0c3 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -43,9 +43,9 @@ namespace osu.Game.Skinning void Add(ISkinnableDrawable drawable); /// - /// Remove an existing skinnable component to this target. + /// Remove an existing skinnable component from this target. /// - /// The component to add. + /// The component to remove. public void Remove(ISkinnableDrawable component); } } diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index f055fb2533..c195a5516f 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Skinning public void Remove(ISkinnableDrawable component) { if (content == null) - throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); + throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin."); if (!(component is Drawable drawable)) throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable)); From 743b4fbff15bb8d506f4848b1c1ed9a803339253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 22:16:37 +0200 Subject: [PATCH 186/215] Pass correct member name to `ArgumentException`s --- osu.Game/Skinning/SkinnableTargetContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs index c195a5516f..d454e199dc 100644 --- a/osu.Game/Skinning/SkinnableTargetContainer.cs +++ b/osu.Game/Skinning/SkinnableTargetContainer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Skinning throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin."); if (!(component is Drawable drawable)) - throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable)); + throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component)); content.Add(drawable); components.Add(component); @@ -67,7 +67,7 @@ namespace osu.Game.Skinning throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin."); if (!(component is Drawable drawable)) - throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(drawable)); + throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component)); content.Remove(drawable); components.Remove(component); From 3d3c5028e6df652f53706e24d7b8ee0f58d09c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 22:33:26 +0200 Subject: [PATCH 187/215] Trim unnecessary array copy --- osu.Game/Skinning/Editor/SkinEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Editor/SkinEditor.cs b/osu.Game/Skinning/Editor/SkinEditor.cs index c594f8d271..f24b0c71c0 100644 --- a/osu.Game/Skinning/Editor/SkinEditor.cs +++ b/osu.Game/Skinning/Editor/SkinEditor.cs @@ -243,7 +243,7 @@ namespace osu.Game.Skinning.Editor public void DeleteItems(ISkinnableDrawable[] items) { - foreach (var item in items.ToArray()) + foreach (var item in items) availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item); } } From 044770f1a265f6a7f1b4d34858384dee6a43c4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:29:34 +0200 Subject: [PATCH 188/215] Locally suppress warning in `SerializationReader` `SerializationReader` is not written in a form that would support turning nullability checking on for the entire class. The biggest problem there is the inner `DynamicDeserializer` static class, whose members are initialised via an `initialize()` method, which the compiler knows nothing about. For this reason, just opt to suppress the single inspection about returning a `null` from a method with a return type of `string` (rider expects `string?`). It would have been also viable to enable nullability checking for this one method, but that's pretty much the same thing and adds no safety anyways, so just disable the warning to minimise surprise. --- osu.Game/IO/Legacy/SerializationReader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 17cbd19838..00f90f78e3 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -38,6 +38,7 @@ namespace osu.Game.IO.Legacy /// Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. public override string ReadString() { + // ReSharper disable once AssignNullToNotNullAttribute if (ReadByte() == 0) return null; return base.ReadString(); From aaa7c7eb0593c9b04c728aee778ff1de5a08f6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:33:17 +0200 Subject: [PATCH 189/215] Handle null case explicitly in `SpectatorState.Equals()` Uses the usual pattern of two `ReferenceEquals` checks against `this` and `null` before proceeding to inspect field values. Doing this causes the compiler to infer that at the point that field values are checked, `other` can no longer viably be `null`. --- osu.Game/Online/Spectator/SpectatorState.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/SpectatorState.cs b/osu.Game/Online/Spectator/SpectatorState.cs index 96a875bc14..ebb91e4dd2 100644 --- a/osu.Game/Online/Spectator/SpectatorState.cs +++ b/osu.Game/Online/Spectator/SpectatorState.cs @@ -24,7 +24,13 @@ namespace osu.Game.Online.Spectator [Key(2)] public IEnumerable Mods { get; set; } = Enumerable.Empty(); - public bool Equals(SpectatorState other) => BeatmapID == other?.BeatmapID && Mods.SequenceEqual(other?.Mods) && RulesetID == other?.RulesetID; + public bool Equals(SpectatorState other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return BeatmapID == other.BeatmapID && Mods.SequenceEqual(other.Mods) && RulesetID == other.RulesetID; + } public override string ToString() => $"Beatmap:{BeatmapID} Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}"; } From c9facf70f9503bd3928b3ddad35807d21df633b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:35:06 +0200 Subject: [PATCH 190/215] Use conditional nullability attribute As it turns out, C# 8 provides an attribute that allows annotating that an `out` parameter's nullability depends on the method's return value, which is exactly what is desired here. --- osu.Game.Tests/Mods/ModUtilsTest.cs | 2 +- osu.Game/Utils/ModUtils.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 7dcaabca3d..7384471c41 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Mods if (isValid) Assert.IsNull(invalid); else - Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); + Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); } public abstract class CustomMod1 : Mod diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 596880f2e7..1c3558fc90 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -92,7 +92,7 @@ namespace osu.Game.Utils /// The mods to check. /// Invalid mods, if any were found. Can be null if all mods were valid. /// Whether the input mods were all valid. If false, will contain all invalid entries. - public static bool CheckValidForGameplay(IEnumerable mods, out List? invalidMods) + public static bool CheckValidForGameplay(IEnumerable mods, [NotNullWhen(false)] out List? invalidMods) { mods = mods.ToArray(); From f716fb09686682f8b9061b2b532866848db0fdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:38:44 +0200 Subject: [PATCH 191/215] Remove bogus `InternalChildren` null-check `InternalChildren` can't viably be `null`, and if it were, we have bigger problems. The removed null-check was triggering false-positive inspections further down. --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs index eb92097204..6fc59ea0e8 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyTaikoScroller.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy base.Update(); // store X before checking wide enough so if we perform layout there is no positional discrepancy. - float currentX = (InternalChildren?.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; + float currentX = (InternalChildren.FirstOrDefault()?.X ?? 0) - (float)Clock.ElapsedFrameTime * 0.1f; // ensure we have enough sprites if (!InternalChildren.Any() From 483e0dd9431efe347f01ac38ef86e1190b8d8f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:39:36 +0200 Subject: [PATCH 192/215] Pass placeholder hitobject instead of `null` --- .../Skinning/TestSceneTaikoScroller.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs index 4ae3cbd418..14c3599fcd 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneTaikoScroller.cs @@ -5,6 +5,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Skinning.Legacy; using osu.Game.Skinning; @@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning })); AddToggleStep("Toggle passing", passing => this.ChildrenOfType().ForEach(s => s.LastResult.Value = - new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss })); + new JudgementResult(new HitObject(), new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss })); AddToggleStep("toggle playback direction", reversed => this.reversed = reversed); } From f2d0f7db9928b60f84b23b14c07488751df86917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:40:17 +0200 Subject: [PATCH 193/215] Remove list null-checks in `LogoTrackingContainer` test If the null-checks were tripped, the test would crash anyway. It is not possible to call `.Any()` and get a valid result instead of an exception on a null reference. --- .../Visual/UserInterface/TestSceneLogoTrackingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs index 5582cc6826..b46d35a84d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs @@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void moveLogoFacade() { - if (!(logoFacade?.Transforms).Any() && !(transferContainer?.Transforms).Any()) + if (!logoFacade.Transforms.Any() && !transferContainer.Transforms.Any()) { Random random = new Random(); trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); From 43c73f9583e31d55cc384ca8fa445011c449ec6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:42:56 +0200 Subject: [PATCH 194/215] Mark access to exception if task faulted as safe There are seemingly no C#-side compile-time guarantees that it is safe, but if the task's state is `Faulted` (as is checked right before), the exception cannot be null as per the documentation. --- osu.Game/Extensions/TaskExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Extensions/TaskExtensions.cs b/osu.Game/Extensions/TaskExtensions.cs index 76a76c0c52..17f1a491f8 100644 --- a/osu.Game/Extensions/TaskExtensions.cs +++ b/osu.Game/Extensions/TaskExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using osu.Framework.Extensions.ObjectExtensions; namespace osu.Game.Extensions { @@ -50,7 +51,7 @@ namespace osu.Game.Extensions } else if (continuationTask.IsFaulted) { - tcs.TrySetException(continuationTask.Exception); + tcs.TrySetException(continuationTask.Exception.AsNonNull()); } else { From 628e7a71edc56fb2ff2f1aec5d44646b3b1a343f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:45:58 +0200 Subject: [PATCH 195/215] Ignore possible nulls in `Type.GetType()` calls They're mostly used in extensibility scenarios, so everything happens in runtime. There is no better resolution than to crash with a null reference exception. --- osu.Game/IO/Serialization/Converters/TypedListConverter.cs | 3 ++- osu.Game/Rulesets/RulesetInfo.cs | 3 ++- osu.Game/Rulesets/RulesetStore.cs | 3 ++- osu.Game/Skinning/SkinInfo.cs | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 50b28ea74b..174fbf9983 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using osu.Framework.Extensions.ObjectExtensions; namespace osu.Game.IO.Serialization.Converters { @@ -60,7 +61,7 @@ namespace osu.Game.IO.Serialization.Converters throw new JsonException("Expected $type token."); var typeName = lookupTable[(int)tok["$type"]]; - var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); + var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull()); serializer.Populate(itemReader, instance); list.Add(instance); diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 702bf35fa8..59ec9cdd7e 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Testing; namespace osu.Game.Rulesets @@ -27,7 +28,7 @@ namespace osu.Game.Rulesets { if (!Available) return null; - var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo)); + var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo).AsNonNull()); // overwrite the pre-populated RulesetInfo with a potentially database attached copy. ruleset.RulesetInfo = this; diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 4261ee3d47..0a34ca9598 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using osu.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Database; @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets { try { - var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo))).RulesetInfo; + var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo).AsNonNull())).RulesetInfo; r.Name = instanceInfo.Name; r.ShortName = instanceInfo.ShortName; diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index bc57a8e71c..55760876e3 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.IO.Stores; using osu.Game.Configuration; using osu.Game.Database; @@ -32,7 +33,7 @@ namespace osu.Game.Skinning var type = string.IsNullOrEmpty(InstantiationInfo) // handle the case of skins imported before InstantiationInfo was added. ? typeof(LegacySkin) - : Type.GetType(InstantiationInfo); + : Type.GetType(InstantiationInfo).AsNonNull(); if (type == typeof(DefaultLegacySkin)) return (Skin)Activator.CreateInstance(type, this, legacyDefaultResources, resources); From 5b2b701915f1c387856742d93a19a620a2ffb368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:47:35 +0200 Subject: [PATCH 196/215] Ignore possible null in `GetResponseString()` A null there indicates a deserialisation error and therefore due to the catch block immediately succeeding the changed line everything will continue to work as intended. --- osu.Game/Online/API/APIAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 944525c119..1686595512 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -270,7 +270,7 @@ namespace osu.Game.Online.API { try { - return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).AsNonNull().ToObject(); + return JObject.Parse(req.GetResponseString().AsNonNull()).SelectToken("form_error", true).AsNonNull().ToObject(); } catch { From fa6b5515b7752932c0ffb9f035659f3c2ab1aedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:49:41 +0200 Subject: [PATCH 197/215] Ignore possible null from `JsonConvert.DeserializeObject()` Nothing better can be done if a `null` is indeed returned. --- osu.Game/Skinning/Skin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 2944c7a8ec..f43283f624 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -8,6 +8,7 @@ using System.Text; using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -57,7 +58,7 @@ namespace osu.Game.Skinning string jsonContent = Encoding.UTF8.GetString(bytes); - DrawableComponentInfo[skinnableTarget] = JsonConvert.DeserializeObject>(jsonContent).ToArray(); + DrawableComponentInfo[skinnableTarget] = JsonConvert.DeserializeObject>(jsonContent).AsNonNull().ToArray(); } } From b51d0380882c33bae1723af63036533f6ed2d8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:50:25 +0200 Subject: [PATCH 198/215] Ignore possible path-related nulls They're all in test code anyway, so any issue there will cause a test to fail. --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 3 ++- osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 3 ++- osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 0c35e9471d..0d117f8755 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Database; @@ -264,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO // change filename var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First()); - firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}")); + firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}")); using (var zip = ZipArchive.Create()) { diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 1c5e551042..a97f6defe9 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -9,6 +9,7 @@ using System.Reflection; using Newtonsoft.Json; using NUnit.Framework; using osu.Framework.Audio.Track; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; @@ -164,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps private Stream openResource(string name) { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); + var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 748a52d1c5..e10bf08da4 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Reflection; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; @@ -41,7 +42,7 @@ namespace osu.Game.Tests.Beatmaps private Stream openResource(string name) { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); + var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } From e62e473bb262f368988dcfdbd417ca272f8a748a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:51:06 +0200 Subject: [PATCH 199/215] Ignore possible null in multiplayer test A null value will fail the test anyhow. --- .../NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 14589f8e6c..adc1d6aede 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -4,6 +4,7 @@ using System.Linq; using Humanizer; using NUnit.Framework; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Testing; using osu.Game.Online.Multiplayer; using osu.Game.Tests.Visual.Multiplayer; @@ -34,7 +35,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer changeState(6, MultiplayerUserState.WaitingForLoad); checkPlayingUserCount(6); - AddStep("another user left", () => Client.RemoveUser(Client.Room?.Users.Last().User)); + AddStep("another user left", () => Client.RemoveUser((Client.Room?.Users.Last().User).AsNonNull())); checkPlayingUserCount(5); AddStep("leave room", () => Client.LeaveRoom()); From d581e0a2524c6c584667d5017e9ab92e81a8de0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:52:16 +0200 Subject: [PATCH 200/215] Ignore possible nulls in `NotifyCollectionChangedArgs` Safe to access by the virtue of the preceding case labels on `args.Action`. And they're in test code anyways. --- .../Visual/Online/TestSceneLeaderboardModSelector.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index 54e655d4ec..fc438ce6dd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -45,14 +46,14 @@ namespace osu.Game.Tests.Visual.Online switch (args.Action) { case NotifyCollectionChangedAction.Add: - args.NewItems.Cast().ForEach(mod => selectedMods.Add(new OsuSpriteText + args.NewItems.AsNonNull().Cast().ForEach(mod => selectedMods.Add(new OsuSpriteText { Text = mod.Acronym, })); break; case NotifyCollectionChangedAction.Remove: - args.OldItems.Cast().ForEach(mod => + args.OldItems.AsNonNull().Cast().ForEach(mod => { foreach (var selected in selectedMods) { From ae71389ebe18bcd3f3c1ca3793088c13fe041fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 14 May 2021 23:53:37 +0200 Subject: [PATCH 201/215] Ignore possible nulls from stream reader in IPC Any failures will be caught. They're not logged, but they also weren't before. Error handling can be improved at a future date, this series of changes is primarily intending to unblock a new inspection. --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 71417d1cc6..f538d4a7d9 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -7,6 +7,7 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.Win32; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; @@ -77,8 +78,8 @@ namespace osu.Game.Tournament.IPC using (var stream = IPCStorage.GetStream(file_ipc_filename)) using (var sr = new StreamReader(stream)) { - var beatmapId = int.Parse(sr.ReadLine()); - var mods = int.Parse(sr.ReadLine()); + var beatmapId = int.Parse(sr.ReadLine().AsNonNull()); + var mods = int.Parse(sr.ReadLine().AsNonNull()); if (lastBeatmapId != beatmapId) { @@ -124,7 +125,7 @@ namespace osu.Game.Tournament.IPC using (var stream = IPCStorage.GetStream(file_ipc_state_filename)) using (var sr = new StreamReader(stream)) { - State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine()); + State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine().AsNonNull()); } } catch (Exception) From 69fc072429bc31ad23a67a1dacf165cf341d6fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 15 May 2021 01:01:27 +0200 Subject: [PATCH 202/215] Ignore skin component json data if deserialisation fails instead Crashing was not really the best thing to do there given the preceding code that already allowed a few continues in case of a missing file. --- osu.Game/Skinning/Skin.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index f43283f624..b6cb8fc7a4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -8,7 +8,6 @@ using System.Text; using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -57,8 +56,12 @@ namespace osu.Game.Skinning continue; string jsonContent = Encoding.UTF8.GetString(bytes); + var deserializedContent = JsonConvert.DeserializeObject>(jsonContent); - DrawableComponentInfo[skinnableTarget] = JsonConvert.DeserializeObject>(jsonContent).AsNonNull().ToArray(); + if (deserializedContent == null) + continue; + + DrawableComponentInfo[skinnableTarget] = deserializedContent.ToArray(); } } From 63ac430386a631ea683496527aa7f8cb47b5f065 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sat, 15 May 2021 11:26:16 +0800 Subject: [PATCH 203/215] Rename startTime in parameters --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 4168293749..2752feb0a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Mods } } - private (double startTime, double fadeDuration) getFadeOutParameters(DrawableOsuHitObject drawableObject) + private (double fadeStartTime, double fadeDuration) getFadeOutParameters(DrawableOsuHitObject drawableObject) { switch (drawableObject) { @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Mods return getParameters(drawableObject.HitObject); } - static (double startTime, double fadeDuration) getParameters(OsuHitObject hitObject) + static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject) { var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; From 6e5c4ed7c6c3e4890c1480d7ccac1d52b131c8cf Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sat, 15 May 2021 11:37:04 +0800 Subject: [PATCH 204/215] Revert "Remove empty override" This reverts commit a86a4bab9137ef6781404dc116101662c4c6d858. --- osu.Game/Rulesets/Mods/ModHidden.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 238612b3d2..03932baca9 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -36,5 +37,13 @@ namespace osu.Game.Rulesets.Mods return rank; } } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } } } From 166974506ea25739eb3ad48c7e9206370a684b4b Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Sat, 15 May 2021 11:51:39 +0800 Subject: [PATCH 205/215] Duplicate implementions --- osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs | 6 ++++-- osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs | 9 +++++++++ osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs | 9 +++++++++ osu.Game/Rulesets/Mods/ModHidden.cs | 9 --------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index ba3e8f9a8f..7bad4c79cb 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -28,10 +28,12 @@ namespace osu.Game.Rulesets.Catch.Mods catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false; } + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - base.ApplyNormalVisibilityState(hitObject, state); - if (!(hitObject is DrawableCatchHitObject catchDrawable)) return; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs index 87501d07a5..3c24e91d54 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Mods @@ -39,5 +40,13 @@ namespace osu.Game.Rulesets.Mania.Mods })); } } + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index a6f902208c..7739ecaf5b 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.Mods { @@ -10,5 +11,13 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Description => @"Beats fade out before you hit them!"; public override double ScoreMultiplier => 1.06; public override bool HasImplementation => false; + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) + { + } } } diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 03932baca9..238612b3d2 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -37,13 +36,5 @@ namespace osu.Game.Rulesets.Mods return rank; } } - - protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } - - protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) - { - } } } From 34d14907548cef21ae02cd8bf3eb7d26a3d16a3b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 10:00:04 +0300 Subject: [PATCH 206/215] Remove null conditional Shouldn't guard against that here. --- osu.Game/Screens/Play/BeatmapMetadataDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index e26dd24de7..fd1150650c 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(BeatmapDifficultyCache difficultyCache) { - var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap.BeatmapInfo.Metadata; AutoSizeAxes = Axes.Both; Children = new Drawable[] From a91f2d3dbad5e4a41dec719fa57ba5279b64ccd2 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 16 May 2021 10:17:04 +0800 Subject: [PATCH 207/215] Change "judgment" to "judgement" --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index ea3eb5eb5c..8993a9b18a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.UI var hitWindows = new OsuHitWindows(); foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) - poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgmentLoaded)); + poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded)); AddRangeInternal(poolDictionary.Values); @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.UI } } - private void onJudgmentLoaded(DrawableOsuJudgement judgement) + private void onJudgementLoaded(DrawableOsuJudgement judgement) { judgementAboveHitObjectLayer.Add(judgement.GetProxyAboveHitObjectsContent()); } From 3519398a224ba44f2f2e91ba6dd3aa921741a6a9 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 16 May 2021 11:16:12 +0800 Subject: [PATCH 208/215] Added "flip" mod for taiko --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs | 32 ++++++++++++++++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs new file mode 100644 index 0000000000..1f2f2c4784 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs @@ -0,0 +1,32 @@ +// 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.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Mods +{ + public class TaikoModFlip : Mod, IApplicableToBeatmap + { + public override string Name => "Flip"; + public override string Acronym => "FP"; + public override string Description => @"Dons become kats, kats become dons"; + public override ModType Type => ModType.Conversion; + public override double ScoreMultiplier => 1; + + public void ApplyToBeatmap(IBeatmap beatmap) + { + var taikoBeatmap = (TaikoBeatmap)beatmap; + + foreach (var obj in taikoBeatmap.HitObjects) + { + if (obj is Hit hit) + hit.Type = hit.Type == HitType.Centre ? HitType.Rim : HitType.Centre; + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index f4e158ec32..26dc1bd416 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Taiko case ModType.Conversion: return new Mod[] { - new TaikoModRandom(), + new MultiMod(new TaikoModFlip(), new TaikoModRandom()), new TaikoModDifficultyAdjust(), new TaikoModClassic(), }; From da13be6dd0d9ec1dca90b46649e9a3e6a3f6c31d Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 16 May 2021 11:28:11 +0800 Subject: [PATCH 209/215] Trimmed trailing white space --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs index 1f2f2c4784..e2de6ccc3d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Mods foreach (var obj in taikoBeatmap.HitObjects) { if (obj is Hit hit) - hit.Type = hit.Type == HitType.Centre ? HitType.Rim : HitType.Centre; + hit.Type = hit.Type == HitType.Centre ? HitType.Rim : HitType.Centre; } } } From 3d83741a23c6c0be53c71000148a3d7531e580c2 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 16 May 2021 12:03:03 +0800 Subject: [PATCH 210/215] Separate Flip and Random --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs | 3 +++ osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs | 3 +++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs index e2de6ccc3d..22e627ef06 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs @@ -1,6 +1,8 @@ // 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.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -17,6 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override string Description => @"Dons become kats, kats become dons"; public override ModType Type => ModType.Conversion; public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs index 1cf19ac18e..e203bbc431 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs @@ -1,6 +1,8 @@ // 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.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; @@ -12,6 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRandom : ModRandom, IApplicableToBeatmap { public override string Description => @"Shuffle around the colours!"; + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModFlip)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 26dc1bd416..52088b7ac4 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -133,7 +133,8 @@ namespace osu.Game.Rulesets.Taiko case ModType.Conversion: return new Mod[] { - new MultiMod(new TaikoModFlip(), new TaikoModRandom()), + new TaikoModFlip(), + new TaikoModRandom(), new TaikoModDifficultyAdjust(), new TaikoModClassic(), }; From cbc2a38b590429ccf07d4aea7e8d4083899aba01 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 May 2021 13:21:06 +0900 Subject: [PATCH 211/215] Move new mod to end to avoid reordering --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 52088b7ac4..9232ea63ee 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -133,10 +133,10 @@ namespace osu.Game.Rulesets.Taiko case ModType.Conversion: return new Mod[] { - new TaikoModFlip(), new TaikoModRandom(), new TaikoModDifficultyAdjust(), new TaikoModClassic(), + new TaikoModFlip(), }; case ModType.Automation: From 422a3b76b64532f69b1432ff01d2c0d39aa68f12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 16 May 2021 13:21:19 +0900 Subject: [PATCH 212/215] Remove unused using statements --- osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs index 22e627ef06..bfab98fbf8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs @@ -3,9 +3,7 @@ using System; using System.Linq; -using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; From 5972e43bc2a038df5e8c2c8f97e17ae28fe79399 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Sun, 16 May 2021 12:51:40 +0800 Subject: [PATCH 213/215] Renamed TaikoModFlip to TaikoModInvert --- osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs | 12 +----------- .../Mods/{TaikoModFlip.cs => TaikoModInvert.cs} | 6 +----- osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Rulesets/Mods/ModInvert.cs | 16 ++++++++++++++++ 5 files changed, 20 insertions(+), 18 deletions(-) rename osu.Game.Rulesets.Taiko/Mods/{TaikoModFlip.cs => TaikoModInvert.cs} (77%) create mode 100644 osu.Game/Rulesets/Mods/ModInvert.cs diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs index 1ea45c295c..0366c0f374 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Graphics.Sprites; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; @@ -13,19 +12,10 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModInvert : Mod, IApplicableAfterBeatmapConversion + public class ManiaModInvert : ModInvert, IApplicableAfterBeatmapConversion { - public override string Name => "Invert"; - - public override string Acronym => "IN"; - public override double ScoreMultiplier => 1; - public override string Description => "Hold the keys. To the beat."; - public override IconUsage? Icon => FontAwesome.Solid.YinYang; - - public override ModType Type => ModType.Conversion; - public void ApplyToBeatmap(IBeatmap beatmap) { var maniaBeatmap = (ManiaBeatmap)beatmap; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs similarity index 77% rename from osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs rename to osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs index bfab98fbf8..a6ccebaa61 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs @@ -10,13 +10,9 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModFlip : Mod, IApplicableToBeatmap + public class TaikoModInvert : ModInvert, IApplicableToBeatmap { - public override string Name => "Flip"; - public override string Acronym => "FP"; public override string Description => @"Dons become kats, kats become dons"; - public override ModType Type => ModType.Conversion; - public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs index e203bbc431..a5ab176176 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRandom : ModRandom, IApplicableToBeatmap { public override string Description => @"Shuffle around the colours!"; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModFlip)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModInvert)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 9232ea63ee..8a716bf56e 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModRandom(), new TaikoModDifficultyAdjust(), new TaikoModClassic(), - new TaikoModFlip(), + new TaikoModInvert(), }; case ModType.Automation: diff --git a/osu.Game/Rulesets/Mods/ModInvert.cs b/osu.Game/Rulesets/Mods/ModInvert.cs new file mode 100644 index 0000000000..6c211d2173 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModInvert.cs @@ -0,0 +1,16 @@ +// 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.Graphics.Sprites; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModInvert : Mod + { + public override string Name => "Invert"; + public override string Acronym => "IN"; + public override ModType Type => ModType.Conversion; + public override IconUsage? Icon => FontAwesome.Solid.YinYang; + public override double ScoreMultiplier => 1; + } +} From c4ae70a82720a04a82502d8bacf158040ac412d9 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 17 May 2021 10:59:56 +0800 Subject: [PATCH 214/215] Revert "Renamed TaikoModFlip to TaikoModInvert" This reverts commit 5972e43bc2a038df5e8c2c8f97e17ae28fe79399. --- osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs | 12 +++++++++++- .../Mods/{TaikoModInvert.cs => TaikoModFlip.cs} | 6 +++++- osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs | 2 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- osu.Game/Rulesets/Mods/ModInvert.cs | 16 ---------------- 5 files changed, 18 insertions(+), 20 deletions(-) rename osu.Game.Rulesets.Taiko/Mods/{TaikoModInvert.cs => TaikoModFlip.cs} (77%) delete mode 100644 osu.Game/Rulesets/Mods/ModInvert.cs diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs index 0366c0f374..1ea45c295c 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModInvert.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics.Sprites; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; @@ -12,10 +13,19 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModInvert : ModInvert, IApplicableAfterBeatmapConversion + public class ManiaModInvert : Mod, IApplicableAfterBeatmapConversion { + public override string Name => "Invert"; + + public override string Acronym => "IN"; + public override double ScoreMultiplier => 1; + public override string Description => "Hold the keys. To the beat."; + public override IconUsage? Icon => FontAwesome.Solid.YinYang; + + public override ModType Type => ModType.Conversion; + public void ApplyToBeatmap(IBeatmap beatmap) { var maniaBeatmap = (ManiaBeatmap)beatmap; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs similarity index 77% rename from osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs rename to osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs index a6ccebaa61..bfab98fbf8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModInvert.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs @@ -10,9 +10,13 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModInvert : ModInvert, IApplicableToBeatmap + public class TaikoModFlip : Mod, IApplicableToBeatmap { + public override string Name => "Flip"; + public override string Acronym => "FP"; public override string Description => @"Dons become kats, kats become dons"; + public override ModType Type => ModType.Conversion; + public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs index a5ab176176..e203bbc431 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRandom : ModRandom, IApplicableToBeatmap { public override string Description => @"Shuffle around the colours!"; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModInvert)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModFlip)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 8a716bf56e..9232ea63ee 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModRandom(), new TaikoModDifficultyAdjust(), new TaikoModClassic(), - new TaikoModInvert(), + new TaikoModFlip(), }; case ModType.Automation: diff --git a/osu.Game/Rulesets/Mods/ModInvert.cs b/osu.Game/Rulesets/Mods/ModInvert.cs deleted file mode 100644 index 6c211d2173..0000000000 --- a/osu.Game/Rulesets/Mods/ModInvert.cs +++ /dev/null @@ -1,16 +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.Graphics.Sprites; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModInvert : Mod - { - public override string Name => "Invert"; - public override string Acronym => "IN"; - public override ModType Type => ModType.Conversion; - public override IconUsage? Icon => FontAwesome.Solid.YinYang; - public override double ScoreMultiplier => 1; - } -} From f34637ea9c06a7e3a00b45f3fc762a025548dfe5 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Mon, 17 May 2021 11:04:01 +0800 Subject: [PATCH 215/215] Renamed TaikoModFlip to TaikoModSwap --- osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs | 2 +- .../Mods/{TaikoModFlip.cs => TaikoModSwap.cs} | 6 +++--- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game.Rulesets.Taiko/Mods/{TaikoModFlip.cs => TaikoModSwap.cs} (86%) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs index e203bbc431..a22f189d5e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModRandom : ModRandom, IApplicableToBeatmap { public override string Description => @"Shuffle around the colours!"; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModFlip)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModSwap)).ToArray(); public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModSwap.cs similarity index 86% rename from osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs rename to osu.Game.Rulesets.Taiko/Mods/TaikoModSwap.cs index bfab98fbf8..3cb337c41d 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlip.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModSwap.cs @@ -10,10 +10,10 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModFlip : Mod, IApplicableToBeatmap + public class TaikoModSwap : Mod, IApplicableToBeatmap { - public override string Name => "Flip"; - public override string Acronym => "FP"; + public override string Name => "Swap"; + public override string Acronym => "SW"; public override string Description => @"Dons become kats, kats become dons"; public override ModType Type => ModType.Conversion; public override double ScoreMultiplier => 1; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 9232ea63ee..5854d4770c 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Taiko new TaikoModRandom(), new TaikoModDifficultyAdjust(), new TaikoModClassic(), - new TaikoModFlip(), + new TaikoModSwap(), }; case ModType.Automation: