From 9806d94b745348e7c44af36cc93b76401234d225 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 15 May 2021 17:56:19 +0300 Subject: [PATCH 01/56] Move beatmap skin info creation to static method at `IBeatmapSkin` --- osu.Game/Skinning/BeatmapSkinExtensions.cs | 16 ++++++++++++++++ osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 +---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Skinning/BeatmapSkinExtensions.cs diff --git a/osu.Game/Skinning/BeatmapSkinExtensions.cs b/osu.Game/Skinning/BeatmapSkinExtensions.cs new file mode 100644 index 0000000000..18ef09c392 --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkinExtensions.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.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + public static class BeatmapSkinExtensions + { + public static SkinInfo CreateSkinInfo(BeatmapInfo beatmap) => new SkinInfo + { + Name = beatmap.ToString(), + Creator = beatmap.Metadata?.AuthorString, + }; + } +} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 3ec205e897..5ee436e8bb 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -17,7 +17,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -49,8 +49,5 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } - - private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => - new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } } From 825d61e22c92646d3166f65d7a44ae631b8fb343 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 12:06:09 +0300 Subject: [PATCH 02/56] Separate default beatmap skins from `DefaultSkin` --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- osu.Game/Skinning/BeatmapSkin.cs | 32 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Skinning/BeatmapSkin.cs diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 3576b149bf..ead8572c54 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(null); + protected virtual ISkin GetSkin() => new BeatmapSkin(BeatmapInfo); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs new file mode 100644 index 0000000000..14b845faeb --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkin.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.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Audio; +using osu.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + /// + /// An empty implementation of a beatmap skin, serves as a temporary default for s. + /// + /// + /// This should be removed once becomes instantiable or a new skin type for osu!lazer beatmaps is defined. + /// + public class BeatmapSkin : Skin + { + public BeatmapSkin(BeatmapInfo beatmap) + : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), null) + { + } + + public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public override IBindable GetConfig(TLookup lookup) => null; + + public override ISample GetSample(ISampleInfo sampleInfo) => null; + } +} From ec89a149dd752d242e1675a4462c8af4e360eb3e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 13:36:10 +0300 Subject: [PATCH 03/56] Add failing test case --- .../TestSceneCatchPlayerLegacySkin.cs | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index 64695153b5..a0b6b6dbe1 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,7 +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.Graphics; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Skinning.Legacy; +using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -9,6 +15,38 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { - protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); + [Test] + public void TestUsingLegacySkin() + { + // check for the existence of a random legacy component to ensure using legacy skin. + // this should exist in LegacySkinPlayerTestScene but the weird transformer logic below needs to be "fixed" or otherwise first. + AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); + } + + protected override Ruleset CreatePlayerRuleset() => new TestCatchRuleset(); + + private class TestCatchRuleset : CatchRuleset + { + public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestCatchLegacySkinTransformer(source); + } + + private class TestCatchLegacySkinTransformer : CatchLegacySkinTransformer + { + public TestCatchLegacySkinTransformer(ISkinSource source) + : base(source) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + var drawable = base.GetDrawableComponent(component); + if (drawable != null) + return drawable; + + // it shouldn't really matter whether to return null or return this, + // but returning null skips over the beatmap skin, so this needs to exist to test things properly. + return Source.GetDrawableComponent(component); + } + } } } From 434377aa52300a5af7698733722f462196434840 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 21:35:06 +0300 Subject: [PATCH 04/56] Revert "Add failing test case" This reverts commit ec89a149dd752d242e1675a4462c8af4e360eb3e. --- .../TestSceneCatchPlayerLegacySkin.cs | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs index a0b6b6dbe1..64695153b5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchPlayerLegacySkin.cs @@ -1,13 +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.Testing; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Skinning.Legacy; -using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -15,38 +9,6 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene { - [Test] - public void TestUsingLegacySkin() - { - // check for the existence of a random legacy component to ensure using legacy skin. - // this should exist in LegacySkinPlayerTestScene but the weird transformer logic below needs to be "fixed" or otherwise first. - AddAssert("using legacy skin", () => this.ChildrenOfType().Any()); - } - - protected override Ruleset CreatePlayerRuleset() => new TestCatchRuleset(); - - private class TestCatchRuleset : CatchRuleset - { - public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestCatchLegacySkinTransformer(source); - } - - private class TestCatchLegacySkinTransformer : CatchLegacySkinTransformer - { - public TestCatchLegacySkinTransformer(ISkinSource source) - : base(source) - { - } - - public override Drawable GetDrawableComponent(ISkinComponent component) - { - var drawable = base.GetDrawableComponent(component); - if (drawable != null) - return drawable; - - // it shouldn't really matter whether to return null or return this, - // but returning null skips over the beatmap skin, so this needs to exist to test things properly. - return Source.GetDrawableComponent(component); - } - } + protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); } } From 6fc06a10a15037bb4890fc2d35ffc46d74bf8df5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 21:52:29 +0300 Subject: [PATCH 05/56] Add extensible test scene for beatmap skins fallback instead --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs new file mode 100644 index 0000000000..760fc5a139 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -0,0 +1,129 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Graphics; +using osu.Framework.Lists; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Extensions; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; +using osu.Game.Skinning; +using osu.Game.Storyboards; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene + { + private ISkin currentBeatmapSkin; + + [Resolved] + private SkinManager skinManager { get; set; } + + [Cached] + private ScoreProcessor scoreProcessor = new ScoreProcessor(); + + [Cached(typeof(HealthProcessor))] + private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); + + protected override bool HasCustomSteps => true; + + [Test] + public void TestEmptyDefaultBeatmapSkinFallsBack() + { + CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); + AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, () => skinManager.CurrentSkin.Value); + } + + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func beatmapSkin) + { + CreateTest(() => + { + AddStep("setup skins", () => + { + skinManager.CurrentSkinInfo.Value = gameCurrentSkin; + currentBeatmapSkin = beatmapSkin(); + }); + }); + } + + protected void AssertComponentsFromExpectedSource(SkinnableTarget target, Func expectedSource) + { + AddAssert($"{target} from {expectedSource.GetType().Name}", () => + { + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource().GetDrawableComponent(new SkinnableTargetComponent(target)); + + Add(expectedComponentsContainer); + expectedComponentsContainer?.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + Remove(expectedComponentsContainer); + + var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().Single().CreateSkinnableInfo(); + + return almostEqual(actualInfo, expectedInfo, 2f); + }); + + static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => + other != null + && info.Anchor == other.Anchor + && info.Origin == other.Origin + && Precision.AlmostEquals(info.Position, other.Position, positionTolerance) + && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer((s1, s2) => almostEqual(s1, s2, positionTolerance))); + } + + protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) + => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin); + + protected override Ruleset CreatePlayerRuleset() => new TestOsuRuleset(); + + private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap + { + private readonly ISkin beatmapSkin; + + public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin) + : base(beatmap, storyboard, referenceClock, audio) + { + this.beatmapSkin = beatmapSkin; + } + + protected override ISkin GetSkin() => beatmapSkin; + } + + private class TestOsuRuleset : OsuRuleset + { + public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TestOsuLegacySkinTransformer(source); + + private class TestOsuLegacySkinTransformer : OsuLegacySkinTransformer + { + public TestOsuLegacySkinTransformer(ISkinSource source) + : base(source) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + var drawable = base.GetDrawableComponent(component); + if (drawable != null) + return drawable; + + // this isn't really supposed to make a difference from returning null, + // but it appears it does, returning null will skip over falling back to beatmap skin, + // while calling Source.GetDrawableComponent() doesn't. + return Source.GetDrawableComponent(component); + } + } + } + } +} From 71da9600009ce891be550e07be5cafda3b9ec065 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 22:53:21 +0300 Subject: [PATCH 06/56] Extract assert step addition out of assertion method --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 760fc5a139..1d0caa86e2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -43,37 +43,34 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestEmptyDefaultBeatmapSkinFallsBack() { CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); - AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, () => skinManager.CurrentSkin.Value); + AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } - protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func beatmapSkin) + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => { AddStep("setup skins", () => { skinManager.CurrentSkinInfo.Value = gameCurrentSkin; - currentBeatmapSkin = beatmapSkin(); + currentBeatmapSkin = getBeatmapSkin(); }); }); } - protected void AssertComponentsFromExpectedSource(SkinnableTarget target, Func expectedSource) + protected bool AssertComponentsFromExpectedSource(SkinnableTarget target, ISkin expectedSource) { - AddAssert($"{target} from {expectedSource.GetType().Name}", () => - { - var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource().GetDrawableComponent(new SkinnableTargetComponent(target)); + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new SkinnableTargetComponent(target)); - Add(expectedComponentsContainer); - expectedComponentsContainer?.UpdateSubTree(); - var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); - Remove(expectedComponentsContainer); + Add(expectedComponentsContainer); + expectedComponentsContainer?.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + Remove(expectedComponentsContainer); - var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) - .ChildrenOfType().Single().CreateSkinnableInfo(); + var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().Single().CreateSkinnableInfo(); - return almostEqual(actualInfo, expectedInfo, 2f); - }); + return almostEqual(actualInfo, expectedInfo, 2f); static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => other != null From 9f3ea150f58247f6de0c7ed14f04282e1ae72c4b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:01:27 +0300 Subject: [PATCH 07/56] Fix legacy beatmap skins not falling back properly on HUD components --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5ee436e8bb..9ff2238e4e 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -23,6 +24,25 @@ namespace osu.Game.Skinning Configuration.AllowDefaultComboColoursFallback = false; } + public override Drawable GetDrawableComponent(ISkinComponent component) + { + if (component is SkinnableTargetComponent targetComponent) + { + switch (targetComponent.Target) + { + case SkinnableTarget.MainHUDComponents: + // this should exist in LegacySkin instead, but there isn't a fallback skin for LegacySkins yet. + // therefore keep the check here until fallback default legacy skin is supported. + if (!this.HasFont(LegacyFont.Score)) + return null; + + break; + } + } + + return base.GetDrawableComponent(component); + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) From 97c84998844240a63a3ec7b32f766d03d657a1f1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 19 May 2021 23:01:41 +0300 Subject: [PATCH 08/56] Add test coverage --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 1d0caa86e2..48b5e67814 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -46,6 +46,13 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); } + [Test] + public void TestEmptyLegacyBeatmapSkinFallsBack() + { + CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null)); + AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); + } + protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => From 262a27610c94dbaae44925e88534166b34318fd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 20:47:40 +0300 Subject: [PATCH 09/56] Improve components assertion logic --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 1d0caa86e2..dd77a71ca5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -7,7 +7,9 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Lists; +using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Framework.Utils; @@ -60,24 +62,41 @@ namespace osu.Game.Tests.Visual.Gameplay protected bool AssertComponentsFromExpectedSource(SkinnableTarget target, ISkin expectedSource) { + var actualComponentsContainer = Player.ChildrenOfType().First(s => s.Target == target) + .ChildrenOfType().SingleOrDefault(); + + if (actualComponentsContainer == null) + return false; + + var actualInfo = actualComponentsContainer.CreateSkinnableInfo(); + var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new SkinnableTargetComponent(target)); + if (expectedComponentsContainer == null) + return false; - Add(expectedComponentsContainer); - expectedComponentsContainer?.UpdateSubTree(); + var expectedComponentsAdjustmentContainer = new Container + { + Position = actualComponentsContainer.Parent.ToSpaceOfOtherDrawable(actualComponentsContainer.DrawPosition, Content), + Size = actualComponentsContainer.DrawSize, + Child = expectedComponentsContainer, + }; + + Add(expectedComponentsAdjustmentContainer); + expectedComponentsAdjustmentContainer?.UpdateSubTree(); var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); - Remove(expectedComponentsContainer); + Remove(expectedComponentsAdjustmentContainer); - var actualInfo = Player.ChildrenOfType().First(s => s.Target == target) - .ChildrenOfType().Single().CreateSkinnableInfo(); + return almostEqual(actualInfo, expectedInfo); - return almostEqual(actualInfo, expectedInfo, 2f); - - static bool almostEqual(SkinnableInfo info, SkinnableInfo other, float positionTolerance) => + static bool almostEqual(SkinnableInfo info, SkinnableInfo other) => other != null + && info.Type == other.Type && info.Anchor == other.Anchor && info.Origin == other.Origin - && Precision.AlmostEquals(info.Position, other.Position, positionTolerance) - && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer((s1, s2) => almostEqual(s1, s2, positionTolerance))); + && Precision.AlmostEquals(info.Position, other.Position) + && Precision.AlmostEquals(info.Scale, other.Scale) + && Precision.AlmostEquals(info.Rotation, other.Rotation) + && info.Children.SequenceEqual(other.Children, new FuncEqualityComparer(almostEqual)); } protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) From eaae9a1b678ae64b3c680c7ff1d172205b122396 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 21:08:31 +0300 Subject: [PATCH 10/56] Remove unrequired null conditional --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index dd77a71ca5..ecdb6fa4af 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -82,8 +82,8 @@ namespace osu.Game.Tests.Visual.Gameplay }; Add(expectedComponentsAdjustmentContainer); - expectedComponentsAdjustmentContainer?.UpdateSubTree(); - var expectedInfo = expectedComponentsContainer?.CreateSkinnableInfo(); + expectedComponentsAdjustmentContainer.UpdateSubTree(); + var expectedInfo = expectedComponentsContainer.CreateSkinnableInfo(); Remove(expectedComponentsAdjustmentContainer); return almostEqual(actualInfo, expectedInfo); From c0dfe379655d9e68496d0de3a6182a764c1f904f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 20 May 2021 21:08:36 +0300 Subject: [PATCH 11/56] Remove unused using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index ecdb6fa4af..22959c082a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -9,7 +9,6 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Lists; -using osu.Framework.Logging; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Framework.Utils; From 49aa0f3a180cd203d6ca1031b0a7ef8d3c7c4deb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 10:04:45 +0700 Subject: [PATCH 12/56] initial main page --- .../Visual/Online/TestSceneWikiMainPage.cs | 36 +++++++++++++++++++ osu.Game/Overlays/Wiki/WikiMainPage.cs | 17 +++++++++ 2 files changed, 53 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs create mode 100644 osu.Game/Overlays/Wiki/WikiMainPage.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs new file mode 100644 index 0000000000..e193f5b98e --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -0,0 +1,36 @@ +// 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.Shapes; +using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneWikiMainPage : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); + + public TestSceneWikiMainPage() + { + Children = new Drawable[] + { + new Box + { + Colour = overlayColour.Background5, + RelativeSizeAxes = Axes.Both, + }, + new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = new WikiMainPage(), + } + }; + } + } +} diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs new file mode 100644 index 0000000000..1ae1631a6e --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiMainPage.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. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiMainPage : FillFlowContainer + { + public WikiMainPage() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + } + } +} From 5964ee23cb2fb95bbecd94115b10d55bfe0344fb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 09:54:54 +0700 Subject: [PATCH 13/56] add HtmlAgilityPack dependency --- osu.Game/osu.Game.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fa2945db6a..e3331cd365 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,6 +19,7 @@ + From 216b87691c203c35f76263859dfbe56dda9ab5d4 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 21 May 2021 10:04:45 +0700 Subject: [PATCH 14/56] add blurb --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 1ae1631a6e..cb6c80cb73 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -3,15 +3,56 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using System.Linq; +using HtmlAgilityPack; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { + public string Markdown; + public WikiMainPage() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; } + + [BackgroundDependencyLoader] + private void load() + { + var html = new HtmlDocument(); + html.LoadHtml(Markdown); + + Children = new Drawable[] + { + createBlurb(html) + }; + } + + private Container createBlurb(HtmlDocument html) + { + var blurbNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page__blurb')]").First(); + + return new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Vertical = 30, + }, + Child = new OsuSpriteText + { + Text = blurbNode.InnerText, + Font = OsuFont.GetFont(size: 12), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + }; + } } } From 318e5fc60bf5455653e8e2ae9846360b0b531542 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:14:26 +0300 Subject: [PATCH 15/56] Mark `WorkingBeatmap.GetSkin()` as abstract --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index ead8572c54..0a55678fb7 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 BeatmapSkin(BeatmapInfo); + protected abstract ISkin GetSkin(); private readonly RecyclableLazy skin; public abstract Stream GetStream(string storagePath); From 04e75d8f2b19ab70dd32a49922dc1157e304fb2b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:16:51 +0300 Subject: [PATCH 16/56] Return empty skin for `GetSkin()` in `TestWorkingBeatmap` --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 852006bc9b..2e4a9fa28f 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -3,9 +3,15 @@ using System.IO; using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +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.Beatmaps; +using osu.Game.Skinning; using osu.Game.Storyboards; namespace osu.Game.Tests.Beatmaps @@ -36,10 +42,23 @@ namespace osu.Game.Tests.Beatmaps protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); + protected override ISkin GetSkin() => new EmptySkin(); + public override Stream GetStream(string storagePath) => null; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; + + private class EmptySkin : ISkin + { + public Drawable GetDrawableComponent(ISkinComponent component) => null; + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public ISample GetSample(ISampleInfo sampleInfo) => null; + + public IBindable GetConfig(TLookup lookup) => null; + } } } From 4f6de6fdc66a0e18aad8f2640dbaa848ea8c8888 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:21:00 +0300 Subject: [PATCH 17/56] Implement `GetSkin()` for other working beatmaps --- osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs | 2 ++ osu.Game.Tests/WaveformTestBeatmap.cs | 3 +++ osu.Game/Beatmaps/BeatmapManager.cs | 1 + osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 3 +++ osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 3 +++ osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 3 +++ 6 files changed, 15 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index a18f82fe4a..059432eeaf 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -169,6 +169,8 @@ namespace osu.Game.Tests.Beatmaps.Formats protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } } diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs index cbed28641c..5477e4a0f8 100644 --- a/osu.Game.Tests/WaveformTestBeatmap.cs +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.IO.Archives; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; @@ -52,6 +53,8 @@ namespace osu.Game.Tests protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile)); + protected override ISkin GetSkin() => null; + public override Stream GetStream(string storagePath) => null; protected override Track GetBeatmapTrack() => trackStore.Get(firstAudioFile); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 5e975de77c..46e3a4f6d7 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -526,6 +526,7 @@ namespace osu.Game.Beatmaps protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; + protected override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; } } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 6922d1c286..ea7f45e53f 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; +using osu.Game.Skinning; namespace osu.Game.Beatmaps { @@ -49,6 +50,8 @@ namespace osu.Game.Beatmaps protected override Track GetBeatmapTrack() => GetVirtualTrack(); + protected override ISkin GetSkin() => null; + public override Stream GetStream(string storagePath) => null; private class DummyRulesetInfo : RulesetInfo diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 66784fda54..6f92db98ee 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -11,6 +11,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.IO; +using osu.Game.Skinning; using Decoder = osu.Game.Beatmaps.Formats.Decoder; namespace osu.Game.Screens.Edit @@ -117,6 +118,8 @@ namespace osu.Game.Screens.Edit protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); } } diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index a97f6defe9..4935f7fc13 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -17,6 +17,7 @@ using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Skinning; namespace osu.Game.Tests.Beatmaps { @@ -216,6 +217,8 @@ namespace osu.Game.Tests.Beatmaps protected override Track GetBeatmapTrack() => throw new NotImplementedException(); + protected override ISkin GetSkin() => throw new NotImplementedException(); + public override Stream GetStream(string storagePath) => throw new NotImplementedException(); protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) From afb33f1641a924b04bd28f3cf52af74c7d730af1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 21 May 2021 20:21:50 +0300 Subject: [PATCH 18/56] Remove no longer necessary test case --- .../Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 22959c082a..bece80903f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; @@ -21,7 +20,6 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; using osu.Game.Storyboards; -using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.Gameplay { @@ -40,13 +38,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected override bool HasCustomSteps => true; - [Test] - public void TestEmptyDefaultBeatmapSkinFallsBack() - { - CreateSkinTest(DefaultLegacySkin.Info, () => new TestWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)).Skin); - AddAssert("hud from default legacy skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value)); - } - protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func getBeatmapSkin) { CreateTest(() => From 0448f6fdb362dcfe370041738f27395949a601fa Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Tue, 25 May 2021 13:21:08 +0700 Subject: [PATCH 19/56] add main page markdown --- osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index e193f5b98e..3a2bafb128 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -28,9 +28,16 @@ namespace osu.Game.Tests.Visual.Online { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), - Child = new WikiMainPage(), + Child = new WikiMainPage + { + Markdown = main_page_markdown + } } }; } + + // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + private const string main_page_markdown = + "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 4a543c25575fe80982ac296a5b3af2e17c1ed61a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:49:26 +0700 Subject: [PATCH 20/56] add simple panels --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index cb6c80cb73..258f6a4f19 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -6,13 +6,23 @@ using osu.Framework.Graphics.Containers; using System.Linq; using HtmlAgilityPack; using osu.Framework.Allocation; +using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Wiki.Markdown; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + public string Markdown; public WikiMainPage() @@ -31,6 +41,7 @@ namespace osu.Game.Overlays.Wiki { createBlurb(html) }; + AddRange(createPanels(html)); } private Container createBlurb(HtmlDocument html) @@ -54,5 +65,50 @@ namespace osu.Game.Overlays.Wiki } }; } + + private IEnumerable createPanels(HtmlDocument html) + { + var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]"); + + foreach (var panel in panelsNode) + { + var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); + + yield return new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = isFullWidth ? 1.0f : 0.5f, + Padding = new MarginPadding(3), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(25), + Offset = new Vector2(0, 1), + Radius = 3, + }, + Child = new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + }, + new WikiMarkdownContainer + { + Text = panel.InnerText, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + } + }; + } + } } } From d9e898a2eea4ee56508255ce9988be9a9978aa0a Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 17:56:27 +0700 Subject: [PATCH 21/56] extract WikiPanelContainer --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 44 ++------------- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 56 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 osu.Game/Overlays/Wiki/WikiPanelContainer.cs diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 258f6a4f19..bdd0b4dc11 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -1,28 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using System.Linq; using HtmlAgilityPack; using osu.Framework.Allocation; -using System.Collections.Generic; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays.Wiki.Markdown; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Wiki { public class WikiMainPage : FillFlowContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - public string Markdown; public WikiMainPage() @@ -74,39 +65,12 @@ namespace osu.Game.Overlays.Wiki { var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); - yield return new Container + yield return new WikiPanelContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Width = isFullWidth ? 1.0f : 0.5f, - Padding = new MarginPadding(3), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 4, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(25), - Offset = new Vector2(0, 1), - Radius = 3, - }, - Child = new Box - { - Colour = colourProvider.Background4, - RelativeSizeAxes = Axes.Both, - }, - }, - new WikiMarkdownContainer - { - Text = panel.InnerText, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - } - } + Text = panel.InnerText, }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs new file mode 100644 index 0000000000..33c9369ccb --- /dev/null +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -0,0 +1,56 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays.Wiki.Markdown; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Wiki +{ + public class WikiPanelContainer : Container + { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + public string Text; + + [BackgroundDependencyLoader] + private void load() + { + Padding = new MarginPadding(3); + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(25), + Offset = new Vector2(0, 1), + Radius = 3, + }, + Child = new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both, + }, + }, + new WikiMarkdownContainer + { + Text = Text, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } + }; + } + } +} From 4d222467ccb6d580de5f86b849058153fdaae745 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:00:54 +0700 Subject: [PATCH 22/56] initial WikiPanelMarkdownContainer --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 33c9369ccb..2d5d806b12 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - new WikiMarkdownContainer + new WikiPanelMarkdownContainer { Text = Text, RelativeSizeAxes = Axes.X, @@ -52,5 +52,15 @@ namespace osu.Game.Overlays.Wiki } }; } + + private class WikiPanelMarkdownContainer : WikiMarkdownContainer + { + public WikiPanelMarkdownContainer() + { + LineSpacing = 0; + DocumentPadding = new MarginPadding(30); + DocumentMargin = new MarginPadding(0); + } + } } } From 365a0b25f28a2bc7043816dde0b1046d52cc0e36 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:03:38 +0700 Subject: [PATCH 23/56] add IsFullWidth --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 1 + osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bdd0b4dc11..c013cbec59 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -71,6 +71,7 @@ namespace osu.Game.Overlays.Wiki AutoSizeAxes = Axes.Y, Width = isFullWidth ? 1.0f : 0.5f, Text = panel.InnerText, + IsFullWidth = isFullWidth, }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 2d5d806b12..b58ba9bb09 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -20,6 +20,8 @@ namespace osu.Game.Overlays.Wiki public string Text; + public bool IsFullWidth; + [BackgroundDependencyLoader] private void load() { @@ -49,12 +51,15 @@ namespace osu.Game.Overlays.Wiki Text = Text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + IsFullWidth = IsFullWidth, } }; } private class WikiPanelMarkdownContainer : WikiMarkdownContainer { + public bool IsFullWidth; + public WikiPanelMarkdownContainer() { LineSpacing = 0; From 1e5f34567ae2f28598c8f08f355c84dffebd3a97 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:07:27 +0700 Subject: [PATCH 24/56] add panel heading main page --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index b58ba9bb09..00afb7b062 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -1,12 +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 Markdig.Syntax; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays.Wiki.Markdown; using osuTK; using osuTK.Graphics; @@ -66,6 +70,33 @@ namespace osu.Game.Overlays.Wiki DocumentPadding = new MarginPadding(30); DocumentMargin = new MarginPadding(0); } + + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) + { + IsFullWidth = IsFullWidth, + }; + } + + private class WikiPanelHeading : OsuMarkdownHeading + { + public bool IsFullWidth; + + public WikiPanelHeading(HeadingBlock headingBlock) + : base(headingBlock) + { + Margin = new MarginPadding { Bottom = 40 }; + } + + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => + { + f.Anchor = Anchor.TopCentre; + f.Origin = Anchor.TopCentre; + f.TextAnchor = Anchor.TopCentre; + }); + + protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Regular; + + protected override float GetFontSizeByLevel(int level) => base.GetFontSizeByLevel(IsFullWidth ? level : 3); } } } From 512d6d2f7fd7b2435b8e4920a59875df6c8c85d1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:09:09 +0700 Subject: [PATCH 25/56] centering link inside panel --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 00afb7b062..31b939d2ac 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -71,6 +71,8 @@ namespace osu.Game.Overlays.Wiki DocumentMargin = new MarginPadding(0); } + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { IsFullWidth = IsFullWidth, From cd211de7295f3dfb3464f0dcd29054423ea2ac86 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:10:56 +0700 Subject: [PATCH 26/56] make panel text bold --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 31b939d2ac..c5dd8549da 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers.Markdown; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers.Markdown; using osu.Game.Overlays.Wiki.Markdown; @@ -71,6 +72,8 @@ namespace osu.Game.Overlays.Wiki DocumentMargin = new MarginPadding(0); } + public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(weight: FontWeight.Bold)); + public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) From 40688810966e0ecb29cc05fc7f17f09d26e77606 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:11:54 +0700 Subject: [PATCH 27/56] add bottom margin for paragraph --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index c5dd8549da..727cae766d 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -76,6 +76,9 @@ namespace osu.Game.Overlays.Wiki public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre); + protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level) + => base.CreateParagraph(paragraphBlock, level).With(p => p.Margin = new MarginPadding { Bottom = 10 }); + protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { IsFullWidth = IsFullWidth, From 8389d90f7ee1d1a6edb3197201c625700cac9907 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 18:52:20 +0700 Subject: [PATCH 28/56] add GridContainer in wiki main page --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c013cbec59..2da5e5e928 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -30,9 +30,13 @@ namespace osu.Game.Overlays.Wiki Children = new Drawable[] { - createBlurb(html) + createBlurb(html), + new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, }; - AddRange(createPanels(html)); } private Container createBlurb(HtmlDocument html) From 24fef221e304ac4d0f1c9fff9b551c8f5d11359f Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 19:07:10 +0700 Subject: [PATCH 29/56] change createPanels method to create grid content --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 48 ++++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 2da5e5e928..12c17d5225 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -61,22 +61,48 @@ namespace osu.Game.Overlays.Wiki }; } - private IEnumerable createPanels(HtmlDocument html) + private IEnumerable createPanels(HtmlDocument html) { - var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]"); + var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); - foreach (var panel in panelsNode) + for (var i = 0; i < panelsNode.Length; i++) { - var isFullWidth = panel.HasClass("wiki-main-page-panel--full"); + var isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); - yield return new WikiPanelContainer + if (isFullWidth) { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = isFullWidth ? 1.0f : 0.5f, - Text = panel.InnerText, - IsFullWidth = isFullWidth, - }; + yield return new Drawable[] + { + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i].InnerText, + IsFullWidth = true, + Width = 2, + }, + null, + }; + } + + if (i % 2 == 1) + { + yield return new Drawable[] + { + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i].InnerText, + }, + new WikiPanelContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Text = panelsNode[i + 1].InnerText, + }, + }; + } } } } From 10c4ba3a746ac11b9899ce5c57523f0830bbaa2d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 19:07:22 +0700 Subject: [PATCH 30/56] add panels to grid content --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 12c17d5225..c4e43d1438 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -28,6 +28,8 @@ namespace osu.Game.Overlays.Wiki var html = new HtmlDocument(); html.LoadHtml(Markdown); + var panels = createPanels(html).ToArray(); + Children = new Drawable[] { createBlurb(html), @@ -35,6 +37,8 @@ namespace osu.Game.Overlays.Wiki { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + RowDimensions = Enumerable.Repeat(new Dimension(GridSizeMode.AutoSize), panels.Length).ToArray(), + Content = panels, }, }; } From 24960c4fb83cfea649ce22a04767abfefc748431 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:31:49 +0700 Subject: [PATCH 31/56] move panel spacing to ctor --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 6 ------ osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 8 +++++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c4e43d1438..8ff85c1404 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -79,8 +79,6 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i].InnerText, IsFullWidth = true, Width = 2, @@ -95,14 +93,10 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i].InnerText, }, new WikiPanelContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, Text = panelsNode[i + 1].InnerText, }, }; diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 727cae766d..64f4d9fbcd 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -27,10 +27,16 @@ namespace osu.Game.Overlays.Wiki public bool IsFullWidth; + public WikiPanelContainer() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding(3); + } + [BackgroundDependencyLoader] private void load() { - Padding = new MarginPadding(3); Children = new Drawable[] { new Container From bd1454bdd1c26de28c5ec91cf6ed89dec218bc94 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:42:58 +0700 Subject: [PATCH 32/56] update height to max of its parent height --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 64f4d9fbcd..f0fecce8dc 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.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 Markdig.Syntax; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -23,6 +24,8 @@ namespace osu.Game.Overlays.Wiki [Resolved] private OverlayColourProvider colourProvider { get; set; } + private WikiPanelMarkdownContainer panelContainer; + public string Text; public bool IsFullWidth; @@ -30,7 +33,6 @@ namespace osu.Game.Overlays.Wiki public WikiPanelContainer() { RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; Padding = new MarginPadding(3); } @@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - new WikiPanelMarkdownContainer + panelContainer = new WikiPanelMarkdownContainer { Text = Text, RelativeSizeAxes = Axes.X, @@ -67,6 +69,12 @@ namespace osu.Game.Overlays.Wiki }; } + protected override void Update() + { + base.Update(); + Height = Math.Max(panelContainer.Height, Parent.DrawHeight); + } + private class WikiPanelMarkdownContainer : WikiMarkdownContainer { public bool IsFullWidth; From 222c34c0a1c77113a0ceb128f37db1acfab547e0 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Sun, 23 May 2021 21:46:41 +0700 Subject: [PATCH 33/56] fix heading font weight to light --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index f0fecce8dc..71b492b375 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.Wiki f.TextAnchor = Anchor.TopCentre; }); - protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Regular; + protected override FontWeight GetFontWeightByLevel(int level) => FontWeight.Light; protected override float GetFontSizeByLevel(int level) => base.GetFontSizeByLevel(IsFullWidth ? level : 3); } From 7792efb154f271e528f4ed0df0ddf966123d5208 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 24 May 2021 12:28:06 +0300 Subject: [PATCH 34/56] Remove no longer used `BeatmapSkin` --- osu.Game/Skinning/BeatmapSkin.cs | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 osu.Game/Skinning/BeatmapSkin.cs diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs deleted file mode 100644 index 14b845faeb..0000000000 --- a/osu.Game/Skinning/BeatmapSkin.cs +++ /dev/null @@ -1,32 +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.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics.OpenGL.Textures; -using osu.Framework.Graphics.Textures; -using osu.Game.Audio; -using osu.Game.Beatmaps; - -namespace osu.Game.Skinning -{ - /// - /// An empty implementation of a beatmap skin, serves as a temporary default for s. - /// - /// - /// This should be removed once becomes instantiable or a new skin type for osu!lazer beatmaps is defined. - /// - public class BeatmapSkin : Skin - { - public BeatmapSkin(BeatmapInfo beatmap) - : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), null) - { - } - - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - - public override IBindable GetConfig(TLookup lookup) => null; - - public override ISample GetSample(ISampleInfo sampleInfo) => null; - } -} From 3585e2900ed8a488d6bb6fb69fddfd6305c37e32 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 24 May 2021 12:28:30 +0300 Subject: [PATCH 35/56] Replace unnecessary empty skin implementation with null --- osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 2e4a9fa28f..bfce59c7de 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -3,13 +3,8 @@ using System.IO; using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; -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.Beatmaps; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -42,23 +37,12 @@ namespace osu.Game.Tests.Beatmaps protected override Storyboard GetStoryboard() => storyboard ?? base.GetStoryboard(); - protected override ISkin GetSkin() => new EmptySkin(); + protected override ISkin GetSkin() => null; public override Stream GetStream(string storagePath) => null; protected override Texture GetBackground() => null; protected override Track GetBeatmapTrack() => null; - - private class EmptySkin : ISkin - { - public Drawable GetDrawableComponent(ISkinComponent component) => null; - - public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; - - public ISample GetSample(ISampleInfo sampleInfo) => null; - - public IBindable GetConfig(TLookup lookup) => null; - } } } From a7865d3f22013bf85fb46ff1433ab3976d2c090b Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 08:46:24 +0700 Subject: [PATCH 36/56] move colour provider to BDL --- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index 71b492b375..ad30306475 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -21,9 +21,6 @@ namespace osu.Game.Overlays.Wiki { public class WikiPanelContainer : Container { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } - private WikiPanelMarkdownContainer panelContainer; public string Text; @@ -37,7 +34,7 @@ namespace osu.Game.Overlays.Wiki } [BackgroundDependencyLoader] - private void load() + private void load(OverlayColourProvider colourProvider) { Children = new Drawable[] { From 0c2d3ae0e757b486b4e28180780387a8addcdf7e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 27 May 2021 05:08:49 +0300 Subject: [PATCH 37/56] Revert "Move beatmap skin info creation to static method at `IBeatmapSkin`" This reverts commit 9806d94b745348e7c44af36cc93b76401234d225. --- osu.Game/Skinning/BeatmapSkinExtensions.cs | 16 ---------------- osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 ++++- 2 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 osu.Game/Skinning/BeatmapSkinExtensions.cs diff --git a/osu.Game/Skinning/BeatmapSkinExtensions.cs b/osu.Game/Skinning/BeatmapSkinExtensions.cs deleted file mode 100644 index 18ef09c392..0000000000 --- a/osu.Game/Skinning/BeatmapSkinExtensions.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.Beatmaps; - -namespace osu.Game.Skinning -{ - public static class BeatmapSkinExtensions - { - public static SkinInfo CreateSkinInfo(BeatmapInfo beatmap) => new SkinInfo - { - Name = beatmap.ToString(), - Creator = beatmap.Metadata?.AuthorString, - }; - } -} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 5ee436e8bb..3ec205e897 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -17,7 +17,7 @@ namespace osu.Game.Skinning protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, IStorageResourceProvider resources) - : base(BeatmapSkinExtensions.CreateSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) + : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), resources, beatmap.Path) { // Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer) Configuration.AllowDefaultComboColoursFallback = false; @@ -49,5 +49,8 @@ namespace osu.Game.Skinning return base.GetSample(sampleInfo); } + + private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => + new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; } } From d66f07fccb10cae2aaf76020bab1c03313a11b56 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 27 May 2021 14:04:50 +0900 Subject: [PATCH 38/56] Move text and `isFullWidth` parameters to constructor --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 14 +++---------- osu.Game/Overlays/Wiki/WikiPanelContainer.cs | 22 ++++++++++++-------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 8ff85c1404..bf4a76c3d8 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -77,10 +77,8 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer + new WikiPanelContainer(panelsNode[i].InnerText, true) { - Text = panelsNode[i].InnerText, - IsFullWidth = true, Width = 2, }, null, @@ -91,14 +89,8 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer - { - Text = panelsNode[i].InnerText, - }, - new WikiPanelContainer - { - Text = panelsNode[i + 1].InnerText, - }, + new WikiPanelContainer(panelsNode[i].InnerText), + new WikiPanelContainer(panelsNode[i + 1].InnerText), }; } } diff --git a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs index ad30306475..db213e4951 100644 --- a/osu.Game/Overlays/Wiki/WikiPanelContainer.cs +++ b/osu.Game/Overlays/Wiki/WikiPanelContainer.cs @@ -23,12 +23,15 @@ namespace osu.Game.Overlays.Wiki { private WikiPanelMarkdownContainer panelContainer; - public string Text; + private readonly string text; - public bool IsFullWidth; + private readonly bool isFullWidth; - public WikiPanelContainer() + public WikiPanelContainer(string text, bool isFullWidth = false) { + this.text = text; + this.isFullWidth = isFullWidth; + RelativeSizeAxes = Axes.X; Padding = new MarginPadding(3); } @@ -56,12 +59,11 @@ namespace osu.Game.Overlays.Wiki RelativeSizeAxes = Axes.Both, }, }, - panelContainer = new WikiPanelMarkdownContainer + panelContainer = new WikiPanelMarkdownContainer(isFullWidth) { - Text = Text, + Text = text, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - IsFullWidth = IsFullWidth, } }; } @@ -74,10 +76,12 @@ namespace osu.Game.Overlays.Wiki private class WikiPanelMarkdownContainer : WikiMarkdownContainer { - public bool IsFullWidth; + private readonly bool isFullWidth; - public WikiPanelMarkdownContainer() + public WikiPanelMarkdownContainer(bool isFullWidth) { + this.isFullWidth = isFullWidth; + LineSpacing = 0; DocumentPadding = new MarginPadding(30); DocumentMargin = new MarginPadding(0); @@ -92,7 +96,7 @@ namespace osu.Game.Overlays.Wiki protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new WikiPanelHeading(headingBlock) { - IsFullWidth = IsFullWidth, + IsFullWidth = isFullWidth, }; } From 4fbd43fcaec8cc6af378cd750bf3165a061ff35d Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 13:24:06 +0700 Subject: [PATCH 39/56] add inline comment for width 2 --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bf4a76c3d8..2356ec03a6 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -79,6 +79,7 @@ namespace osu.Game.Overlays.Wiki { new WikiPanelContainer(panelsNode[i].InnerText, true) { + // This is required to fill up the space of "null" drawable below. Width = 2, }, null, From c72e258bfb857e229bc8c46faffc9d12ec2ae238 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 13:34:55 +0700 Subject: [PATCH 40/56] change for to while in create panels --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 2356ec03a6..76e6b0261a 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -69,7 +69,9 @@ namespace osu.Game.Overlays.Wiki { var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); - for (var i = 0; i < panelsNode.Length; i++) + var i = 0; + + while (i < panelsNode.Length) { var isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); @@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Wiki { yield return new Drawable[] { - new WikiPanelContainer(panelsNode[i].InnerText, true) + new WikiPanelContainer(panelsNode[i++].InnerText, true) { // This is required to fill up the space of "null" drawable below. Width = 2, @@ -85,13 +87,12 @@ namespace osu.Game.Overlays.Wiki null, }; } - - if (i % 2 == 1) + else { yield return new Drawable[] { - new WikiPanelContainer(panelsNode[i].InnerText), - new WikiPanelContainer(panelsNode[i + 1].InnerText), + new WikiPanelContainer(panelsNode[i++].InnerText), + new WikiPanelContainer(panelsNode[i++].InnerText), }; } } From b55ac413e7ca8953450319d3212ed9a1208d1ea2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 14:20:36 +0700 Subject: [PATCH 41/56] add many scenario of main page layout --- .../Visual/Online/TestSceneWikiMainPage.cs | 99 +++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 3a2bafb128..7fa1a6e135 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,7 +16,10 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); - public TestSceneWikiMainPage() + private BasicScrollContainer container; + + [SetUp] + public void Setup() => Schedule(() => { Children = new Drawable[] { @@ -24,20 +28,103 @@ namespace osu.Game.Tests.Visual.Online Colour = overlayColour.Background5, RelativeSizeAxes = Axes.Both, }, - new BasicScrollContainer + container = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), - Child = new WikiMainPage - { - Markdown = main_page_markdown - } } }; + }); + + [Test] + public void TestWikiMainPage() + { + AddStep("Show main page", () => + { + container.Child = new WikiMainPage + { + Markdown = main_page_markdown, + }; + }); + } + + [Test] + public void TestNonFullWidthLayout() + { + AddStep("Show Layout", () => + { + container.Child = new WikiMainPage + { + Markdown = non_full_width, + }; + }); + } + + [Test] + public void TestFullWidthAtTheEnd() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_end, + }; + }); + } + + [Test] + public void TestFullWidthAtTheStartAndEnd() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_start_end, + }; + }); + } + + [Test] + public void TestFullWidthInTheMiddle() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_in_the_middle, + }; + }); + } + + [Test] + public void TestFullWidthStacking() + { + AddStep("Show layout", () => + { + container.Child = new WikiMainPage + { + Markdown = full_width_stack, + }; + }); } // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page private const string main_page_markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string non_full_width = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string full_width_end = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; + + private const string full_width_start_end = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; + + private const string full_width_in_the_middle = + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; + + private const string full_width_stack= + "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 820310543d56b23ed5fc99591da3f4e994a37ad2 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 14:53:22 +0700 Subject: [PATCH 42/56] fix spacing style --- osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 7fa1a6e135..9fb3c685b7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.Online private const string full_width_in_the_middle = "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - private const string full_width_stack= + private const string full_width_stack = "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From 0b600db81487a766e9d91687c7a57e6ae8517cfb Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 16:24:05 +0700 Subject: [PATCH 43/56] revert back main page test --- .../Visual/Online/TestSceneWikiMainPage.cs | 99 ++----------------- 1 file changed, 6 insertions(+), 93 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 9fb3c685b7..3a2bafb128 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -16,10 +15,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange); - private BasicScrollContainer container; - - [SetUp] - public void Setup() => Schedule(() => + public TestSceneWikiMainPage() { Children = new Drawable[] { @@ -28,103 +24,20 @@ namespace osu.Game.Tests.Visual.Online Colour = overlayColour.Background5, RelativeSizeAxes = Axes.Both, }, - container = new BasicScrollContainer + new BasicScrollContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(20), + Child = new WikiMainPage + { + Markdown = main_page_markdown + } } }; - }); - - [Test] - public void TestWikiMainPage() - { - AddStep("Show main page", () => - { - container.Child = new WikiMainPage - { - Markdown = main_page_markdown, - }; - }); - } - - [Test] - public void TestNonFullWidthLayout() - { - AddStep("Show Layout", () => - { - container.Child = new WikiMainPage - { - Markdown = non_full_width, - }; - }); - } - - [Test] - public void TestFullWidthAtTheEnd() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_end, - }; - }); - } - - [Test] - public void TestFullWidthAtTheStartAndEnd() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_start_end, - }; - }); - } - - [Test] - public void TestFullWidthInTheMiddle() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_in_the_middle, - }; - }); - } - - [Test] - public void TestFullWidthStacking() - { - AddStep("Show layout", () => - { - container.Child = new WikiMainPage - { - Markdown = full_width_stack, - }; - }); } // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page private const string main_page_markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string non_full_width = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string full_width_end = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; - - private const string full_width_start_end = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n"; - - private const string full_width_in_the_middle = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; - - private const string full_width_stack = - "
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } } From e1836cd1b2535a2d0329e9d0a71979bd2796a414 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 27 May 2021 17:12:15 +0700 Subject: [PATCH 44/56] add debug assert --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index 76e6b0261a..bbbbc602bf 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.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 System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -69,6 +70,8 @@ namespace osu.Game.Overlays.Wiki { var panelsNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page-panel')]").ToArray(); + Debug.Assert(panelsNode.Length > 1); + var i = 0; while (i < panelsNode.Length) From ead0e92d7d523b3a29a2f44e82a16fbd815ec8ff Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 28 May 2021 00:50:59 +0700 Subject: [PATCH 45/56] use select single node for blurb --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index bbbbc602bf..b34b5d9acf 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Wiki private Container createBlurb(HtmlDocument html) { - var blurbNode = html.DocumentNode.SelectNodes("//div[contains(@class, 'wiki-main-page__blurb')]").First(); + var blurbNode = html.DocumentNode.SelectSingleNode("//div[contains(@class, 'wiki-main-page__blurb')]"); return new Container { From 854ef0abfdc67cf87c7e20552c6db320707eb8ba Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Fri, 28 May 2021 00:55:14 +0700 Subject: [PATCH 46/56] add simple bound check --- osu.Game/Overlays/Wiki/WikiMainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index b34b5d9acf..c4c0b83ef4 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Wiki yield return new Drawable[] { new WikiPanelContainer(panelsNode[i++].InnerText), - new WikiPanelContainer(panelsNode[i++].InnerText), + i < panelsNode.Length ? new WikiPanelContainer(panelsNode[i++].InnerText) : null, }; } } From 5e8893b20336249b6139b22d2269d00b25e243ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 28 May 2021 10:42:13 +0900 Subject: [PATCH 47/56] 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 b3842a528d..bcd5f9bd9a 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 fa2945db6a..5f0f3028f0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -33,7 +33,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e35b1b5c42..59b026e0ad 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1d5e8f4a912584428dee430764e49e5c6286a17b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 28 May 2021 17:19:36 +0900 Subject: [PATCH 48/56] Fix wrong/misleading comment of `InitialLifetimeOffset` --- .../Objects/Drawables/DrawableHitObject.cs | 15 +++------------ .../Rulesets/Objects/HitObjectLifetimeEntry.cs | 5 ++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index cca55819c5..5faefbf469 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -11,7 +11,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Primitives; using osu.Framework.Threading; using osu.Game.Audio; @@ -445,9 +444,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Apply (generally fade-in) transforms leading into the start time. - /// The local drawable hierarchy is recursively delayed to for convenience. - /// - /// By default this will fade in the object from zero with no duration. + /// By default, this will fade in the object from zero with no duration. /// /// /// This is called once before every . This is to ensure a good state in the case @@ -623,17 +620,11 @@ namespace osu.Game.Rulesets.Objects.Drawables protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action); /// - /// A safe offset prior to the start time of at which this may begin displaying contents. + /// A offset prior to the start time of at which this may begin displaying contents. /// By default, s are assumed to display their contents within 10 seconds prior to the start time of . /// /// - /// This is only used as an optimisation to delay the initial update of this and may be tuned more aggressively if required. - /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). - /// - /// Only has an effect if this is not being pooled. - /// For pooled s, use instead. - /// + /// The initial transformation () starts at this offset before the start time of . /// protected virtual double InitialLifetimeOffset => 10000; diff --git a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs index 0d1eb68f07..cf62226d94 100644 --- a/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs +++ b/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs @@ -82,9 +82,8 @@ namespace osu.Game.Rulesets.Objects /// By default, s are assumed to display their contents within 10 seconds prior to their start time. /// /// - /// This is only used as an optimisation to delay the initial update of the and may be tuned more aggressively if required. - /// It is indirectly used to decide the automatic transform offset provided to . - /// A more accurate should be set for further optimisation (in , for example). + /// This is only used as an optimisation to delay the initial application of the to a . + /// A more accurate should be set on the hit object application, for further optimisation. /// protected virtual double InitialLifetimeOffset => 10000; From 2439de1c3dd3ce3ff085d41c6c1aa95e2cf1d1e2 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Fri, 28 May 2021 18:40:58 +0200 Subject: [PATCH 49/56] fix capitalzation of osu!catch HitResults --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index e3c457693e..23ce444560 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -161,13 +161,13 @@ namespace osu.Game.Rulesets.Catch switch (result) { case HitResult.LargeTickHit: - return "large droplet"; + return "Large droplet"; case HitResult.SmallTickHit: - return "small droplet"; + return "Small droplet"; case HitResult.LargeBonus: - return "banana"; + return "Banana"; } return base.GetDisplayNameForHitResult(result); From 951fc5ef6ecff428a20b7b52c8f26cab9c170fe9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 29 May 2021 15:39:12 +0900 Subject: [PATCH 50/56] Remove misleading comment and misplaced bug fix This shouldn't be fixed in a test scene; the underlying issue should be fixed in actual game code. --- .../Gameplay/TestSceneBeatmapSkinFallbacks.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index bece80903f..d6bedffaa8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Lists; using osu.Framework.Testing; @@ -117,18 +116,6 @@ namespace osu.Game.Tests.Visual.Gameplay : base(source) { } - - public override Drawable GetDrawableComponent(ISkinComponent component) - { - var drawable = base.GetDrawableComponent(component); - if (drawable != null) - return drawable; - - // this isn't really supposed to make a difference from returning null, - // but it appears it does, returning null will skip over falling back to beatmap skin, - // while calling Source.GetDrawableComponent() doesn't. - return Source.GetDrawableComponent(component); - } } } } From 200fecc774d7e0e79b63dc3267e339a976cc58f4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 May 2021 21:22:39 +0300 Subject: [PATCH 51/56] Fix missing using directive --- osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs index 31d29e6807..cc53e50884 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapSkinFallbacks.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics.Containers; From 53cbf369d7a55e1c6ef5f38af9d35de8ce4739ae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 May 2021 21:22:46 +0300 Subject: [PATCH 52/56] Fix potential nullref --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 04b2346c6a..caf37e5bc9 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -71,6 +71,6 @@ namespace osu.Game.Skinning } private static SkinInfo createSkinInfo(BeatmapInfo beatmap) => - new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }; + new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata?.AuthorString }; } } From fbc316ea1d6cf401b0a3cf75ecb604b51f1c2211 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 27 May 2021 05:15:29 +0300 Subject: [PATCH 53/56] Fix legacy skin transformers potentially ignoring source implementations --- .../Legacy/CatchLegacySkinTransformer.cs | 20 +++++++++++-------- .../Legacy/ManiaLegacySkinTransformer.cs | 8 +++++--- .../Legacy/OsuLegacySkinTransformer.cs | 7 ++++--- .../Legacy/TaikoLegacySkinTransformer.cs | 7 ++++--- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 1b48832ed6..6efd9e88c4 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -28,12 +28,15 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { case HUDSkinComponents.ComboCounter: // catch may provide its own combo counter; hide the default. - return providesComboCounter ? Drawable.Empty() : null; + if (providesComboCounter) + return Drawable.Empty(); + + break; } } if (!(component is CatchSkinComponent catchSkinComponent)) - return null; + return Source.GetDrawableComponent(component); switch (catchSkinComponent.Component) { @@ -41,19 +44,19 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (GetTexture("fruit-pear") != null) return new LegacyFruitPiece(); - break; + return null; case CatchSkinComponents.Banana: if (GetTexture("fruit-bananas") != null) return new LegacyBananaPiece(); - break; + return null; case CatchSkinComponents.Droplet: if (GetTexture("fruit-drop") != null) return new LegacyDropletPiece(); - break; + return null; case CatchSkinComponents.CatcherIdle: return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? @@ -71,10 +74,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (providesComboCounter) return new LegacyCatchComboCounter(Source); - break; - } + return null; - return null; + default: + return Source.GetDrawableComponent(component); + } } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 24ccae895d..6617fcefe7 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -120,12 +120,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); + + default: + return Source.GetDrawableComponent(component); } - break; + default: + return Source.GetDrawableComponent(component); } - - return null; } private Drawable getResult(HitResult result) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 88302ebc57..f1376551f5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { if (!(component is OsuSkinComponent osuComponent)) - return null; + return Source.GetDrawableComponent(component); switch (osuComponent.Component) { @@ -115,9 +115,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return new LegacyOldStyleSpinner(); return null; - } - return null; + default: + return Source.GetDrawableComponent(component); + } } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index 3e506f69ce..05778a80ef 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy } if (!(component is TaikoSkinComponent taikoComponent)) - return null; + return Source.GetDrawableComponent(component); switch (taikoComponent.Component) { @@ -130,9 +130,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.Mascot: return new DrawableTaikoMascot(); - } - return Source.GetDrawableComponent(component); + default: + return Source.GetDrawableComponent(component); + } } private string getHitName(TaikoSkinComponents component) From abb77b95b4c1e3e8a28f4c7b40b320c5ee459bc1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 30 May 2021 13:06:28 +0900 Subject: [PATCH 54/56] Fix comment grammar --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 5faefbf469..6c688c1625 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -620,7 +620,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action); /// - /// A offset prior to the start time of at which this may begin displaying contents. + /// An offset prior to the start time of at which this may begin displaying contents. /// By default, s are assumed to display their contents within 10 seconds prior to the start time of . /// /// From 50d71faf565ea5f11df4d9192379de33d240e5f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 May 2021 17:55:10 +0900 Subject: [PATCH 55/56] Restructure lookup code to avoid repeating the base call --- .../Legacy/CatchLegacySkinTransformer.cs | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 6efd9e88c4..e43d88ad40 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -35,50 +35,49 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy } } - if (!(component is CatchSkinComponent catchSkinComponent)) - return Source.GetDrawableComponent(component); - - switch (catchSkinComponent.Component) + if (component is CatchSkinComponent catchSkinComponent) { - case CatchSkinComponents.Fruit: - if (GetTexture("fruit-pear") != null) - return new LegacyFruitPiece(); + switch (catchSkinComponent.Component) + { + case CatchSkinComponents.Fruit: + if (GetTexture("fruit-pear") != null) + return new LegacyFruitPiece(); - return null; + return null; - case CatchSkinComponents.Banana: - if (GetTexture("fruit-bananas") != null) - return new LegacyBananaPiece(); + case CatchSkinComponents.Banana: + if (GetTexture("fruit-bananas") != null) + return new LegacyBananaPiece(); - return null; + return null; - case CatchSkinComponents.Droplet: - if (GetTexture("fruit-drop") != null) - return new LegacyDropletPiece(); + case CatchSkinComponents.Droplet: + if (GetTexture("fruit-drop") != null) + return new LegacyDropletPiece(); - return null; + return null; - case CatchSkinComponents.CatcherIdle: - return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherIdle: + return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatcherFail: - return this.GetAnimation("fruit-catcher-fail", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherFail: + return this.GetAnimation("fruit-catcher-fail", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatcherKiai: - return this.GetAnimation("fruit-catcher-kiai", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.CatcherKiai: + return this.GetAnimation("fruit-catcher-kiai", true, true, true) ?? + this.GetAnimation("fruit-ryuuta", true, true, true); - case CatchSkinComponents.CatchComboCounter: - if (providesComboCounter) - return new LegacyCatchComboCounter(Source); + case CatchSkinComponents.CatchComboCounter: + if (providesComboCounter) + return new LegacyCatchComboCounter(Source); - return null; - - default: - return Source.GetDrawableComponent(component); + return null; + } } + + return Source.GetDrawableComponent(component); } public override IBindable GetConfig(TLookup lookup) From 17574833fb83dd3f71bcf86acb22b3d44c0603a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 30 May 2021 19:15:59 +0900 Subject: [PATCH 56/56] Update other transformers with similar refactored logic --- .../Legacy/ManiaLegacySkinTransformer.cs | 8 +- .../Legacy/OsuLegacySkinTransformer.cs | 143 +++++++++--------- .../Legacy/TaikoLegacySkinTransformer.cs | 131 ++++++++-------- 3 files changed, 139 insertions(+), 143 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 6617fcefe7..261b8b1fad 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -120,14 +120,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy case ManiaSkinComponents.StageForeground: return new LegacyStageForeground(); - - default: - return Source.GetDrawableComponent(component); } - default: - return Source.GetDrawableComponent(component); + break; } + + return Source.GetDrawableComponent(component); } private Drawable getResult(HitResult result) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index f1376551f5..ffd4f78400 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -34,91 +34,90 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public override Drawable GetDrawableComponent(ISkinComponent component) { - if (!(component is OsuSkinComponent osuComponent)) - return Source.GetDrawableComponent(component); - - switch (osuComponent.Component) + if (component is OsuSkinComponent osuComponent) { - case OsuSkinComponents.FollowPoint: - return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false); + switch (osuComponent.Component) + { + case OsuSkinComponents.FollowPoint: + return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false); - case OsuSkinComponents.SliderFollowCircle: - var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); - if (followCircle != null) - // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x - followCircle.Scale *= 0.5f; - return followCircle; + case OsuSkinComponents.SliderFollowCircle: + var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true); + if (followCircle != null) + // follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x + followCircle.Scale *= 0.5f; + return followCircle; - case OsuSkinComponents.SliderBall: - var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); + case OsuSkinComponents.SliderBall: + var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: ""); - // todo: slider ball has a custom frame delay based on velocity - // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); + // todo: slider ball has a custom frame delay based on velocity + // Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME); - if (sliderBallContent != null) - return new LegacySliderBall(sliderBallContent); + if (sliderBallContent != null) + return new LegacySliderBall(sliderBallContent); - return null; - - case OsuSkinComponents.SliderBody: - if (hasHitCircle.Value) - return new LegacySliderBody(); - - return null; - - case OsuSkinComponents.SliderTailHitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderendcircle", false); - - return null; - - case OsuSkinComponents.SliderHeadHitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderstartcircle"); - - return null; - - case OsuSkinComponents.HitCircle: - if (hasHitCircle.Value) - return new LegacyMainCirclePiece(); - - return null; - - case OsuSkinComponents.Cursor: - if (Source.GetTexture("cursor") != null) - return new LegacyCursor(); - - return null; - - case OsuSkinComponents.CursorTrail: - if (Source.GetTexture("cursortrail") != null) - return new LegacyCursorTrail(); - - return null; - - case OsuSkinComponents.HitCircleText: - if (!this.HasFont(LegacyFont.HitCircle)) return null; - return new LegacySpriteText(LegacyFont.HitCircle) - { - // stable applies a blanket 0.8x scale to hitcircle fonts - Scale = new Vector2(0.8f), - }; + case OsuSkinComponents.SliderBody: + if (hasHitCircle.Value) + return new LegacySliderBody(); - case OsuSkinComponents.SpinnerBody: - bool hasBackground = Source.GetTexture("spinner-background") != null; + return null; - if (Source.GetTexture("spinner-top") != null && !hasBackground) - return new LegacyNewStyleSpinner(); - else if (hasBackground) - return new LegacyOldStyleSpinner(); + case OsuSkinComponents.SliderTailHitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece("sliderendcircle", false); - return null; + return null; - default: - return Source.GetDrawableComponent(component); + case OsuSkinComponents.SliderHeadHitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece("sliderstartcircle"); + + return null; + + case OsuSkinComponents.HitCircle: + if (hasHitCircle.Value) + return new LegacyMainCirclePiece(); + + return null; + + case OsuSkinComponents.Cursor: + if (Source.GetTexture("cursor") != null) + return new LegacyCursor(); + + return null; + + case OsuSkinComponents.CursorTrail: + if (Source.GetTexture("cursortrail") != null) + return new LegacyCursorTrail(); + + return null; + + case OsuSkinComponents.HitCircleText: + if (!this.HasFont(LegacyFont.HitCircle)) + return null; + + return new LegacySpriteText(LegacyFont.HitCircle) + { + // stable applies a blanket 0.8x scale to hitcircle fonts + Scale = new Vector2(0.8f), + }; + + case OsuSkinComponents.SpinnerBody: + bool hasBackground = Source.GetTexture("spinner-background") != null; + + if (Source.GetTexture("spinner-top") != null && !hasBackground) + return new LegacyNewStyleSpinner(); + else if (hasBackground) + return new LegacyOldStyleSpinner(); + + return null; + } } + + return Source.GetDrawableComponent(component); } public override IBindable GetConfig(TLookup lookup) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index 05778a80ef..e0557c8617 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -38,102 +38,101 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy return Drawable.Empty().With(d => d.Expire()); } - if (!(component is TaikoSkinComponent taikoComponent)) - return Source.GetDrawableComponent(component); - - switch (taikoComponent.Component) + if (component is TaikoSkinComponent taikoComponent) { - case TaikoSkinComponents.DrumRollBody: - if (GetTexture("taiko-roll-middle") != null) - return new LegacyDrumRoll(); + switch (taikoComponent.Component) + { + case TaikoSkinComponents.DrumRollBody: + if (GetTexture("taiko-roll-middle") != null) + return new LegacyDrumRoll(); - return null; + return null; - case TaikoSkinComponents.InputDrum: - if (GetTexture("taiko-bar-left") != null) - return new LegacyInputDrum(); + case TaikoSkinComponents.InputDrum: + if (GetTexture("taiko-bar-left") != null) + return new LegacyInputDrum(); - return null; + return null; - case TaikoSkinComponents.CentreHit: - case TaikoSkinComponents.RimHit: + case TaikoSkinComponents.CentreHit: + case TaikoSkinComponents.RimHit: - if (GetTexture("taikohitcircle") != null) - return new LegacyHit(taikoComponent.Component); + if (GetTexture("taikohitcircle") != null) + return new LegacyHit(taikoComponent.Component); - return null; + return null; - case TaikoSkinComponents.DrumRollTick: - return this.GetAnimation("sliderscorepoint", false, false); + case TaikoSkinComponents.DrumRollTick: + return this.GetAnimation("sliderscorepoint", false, false); - case TaikoSkinComponents.HitTarget: - if (GetTexture("taikobigcircle") != null) - return new TaikoLegacyHitTarget(); + case TaikoSkinComponents.HitTarget: + if (GetTexture("taikobigcircle") != null) + return new TaikoLegacyHitTarget(); - return null; + return null; - case TaikoSkinComponents.PlayfieldBackgroundRight: - if (GetTexture("taiko-bar-right") != null) - return new TaikoLegacyPlayfieldBackgroundRight(); + case TaikoSkinComponents.PlayfieldBackgroundRight: + if (GetTexture("taiko-bar-right") != null) + return new TaikoLegacyPlayfieldBackgroundRight(); - return null; + return null; - case TaikoSkinComponents.PlayfieldBackgroundLeft: - // This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins). - if (GetTexture("taiko-bar-right") != null) - return Drawable.Empty(); + case TaikoSkinComponents.PlayfieldBackgroundLeft: + // This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins). + if (GetTexture("taiko-bar-right") != null) + return Drawable.Empty(); - return null; + return null; - case TaikoSkinComponents.BarLine: - if (GetTexture("taiko-barline") != null) - return new LegacyBarLine(); + case TaikoSkinComponents.BarLine: + if (GetTexture("taiko-barline") != null) + return new LegacyBarLine(); - return null; + return null; - case TaikoSkinComponents.TaikoExplosionMiss: + case TaikoSkinComponents.TaikoExplosionMiss: - var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); - if (missSprite != null) - return new LegacyHitExplosion(missSprite); + var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); + if (missSprite != null) + return new LegacyHitExplosion(missSprite); - return null; + return null; - case TaikoSkinComponents.TaikoExplosionOk: - case TaikoSkinComponents.TaikoExplosionGreat: + case TaikoSkinComponents.TaikoExplosionOk: + case TaikoSkinComponents.TaikoExplosionGreat: - var hitName = getHitName(taikoComponent.Component); - var hitSprite = this.GetAnimation(hitName, true, false); + var hitName = getHitName(taikoComponent.Component); + var hitSprite = this.GetAnimation(hitName, true, false); - if (hitSprite != null) - { - var strongHitSprite = this.GetAnimation($"{hitName}k", true, false); + if (hitSprite != null) + { + var strongHitSprite = this.GetAnimation($"{hitName}k", true, false); - return new LegacyHitExplosion(hitSprite, strongHitSprite); - } + return new LegacyHitExplosion(hitSprite, strongHitSprite); + } - return null; + return null; - case TaikoSkinComponents.TaikoExplosionKiai: - // suppress the default kiai explosion if the skin brings its own sprites. - // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. - if (hasExplosion.Value) - return Drawable.Empty().With(d => d.Expire()); + case TaikoSkinComponents.TaikoExplosionKiai: + // suppress the default kiai explosion if the skin brings its own sprites. + // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. + if (hasExplosion.Value) + return Drawable.Empty().With(d => d.Expire()); - return null; + return null; - case TaikoSkinComponents.Scroller: - if (GetTexture("taiko-slider") != null) - return new LegacyTaikoScroller(); + case TaikoSkinComponents.Scroller: + if (GetTexture("taiko-slider") != null) + return new LegacyTaikoScroller(); - return null; + return null; - case TaikoSkinComponents.Mascot: - return new DrawableTaikoMascot(); - - default: - return Source.GetDrawableComponent(component); + case TaikoSkinComponents.Mascot: + return new DrawableTaikoMascot(); + } } + + return Source.GetDrawableComponent(component); } private string getHitName(TaikoSkinComponents component)