diff --git a/osu.Android.props b/osu.Android.props
index ec223f98c2..24d07b4588 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Game.Benchmarks/BenchmarkMod.cs b/osu.Game.Benchmarks/BenchmarkMod.cs
new file mode 100644
index 0000000000..050ddf36d5
--- /dev/null
+++ b/osu.Game.Benchmarks/BenchmarkMod.cs
@@ -0,0 +1,34 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using BenchmarkDotNet.Attributes;
+using osu.Game.Rulesets.Osu.Mods;
+
+namespace osu.Game.Benchmarks
+{
+ public class BenchmarkMod : BenchmarkTest
+ {
+ private OsuModDoubleTime mod;
+
+ [Params(1, 10, 100)]
+ public int Times { get; set; }
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ mod = new OsuModDoubleTime();
+ }
+
+ [Benchmark]
+ public int ModHashCode()
+ {
+ var hashCode = new HashCode();
+
+ for (int i = 0; i < Times; i++)
+ hashCode.Add(mod);
+
+ return hashCode.ToHashCode();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
index d4f1602a46..c89527d8bd 100644
--- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
+++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditorRuleset.cs
@@ -64,11 +64,14 @@ namespace osu.Game.Rulesets.Osu.Edit
if (hitObject is DrawableHitCircle circle)
{
- circle.ApproachCircle
- .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
- .Expire();
+ using (circle.BeginAbsoluteSequence(circle.HitStateUpdateTime))
+ {
+ circle.ApproachCircle
+ .FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
+ .Expire();
- circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
+ circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
+ }
}
if (hitObject is IHasMainCirclePiece mainPieceContainer)
diff --git a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs b/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs
deleted file mode 100644
index d407c0663f..0000000000
--- a/osu.Game.Tests/Beatmaps/BeatmapDifficultyCacheTest.cs
+++ /dev/null
@@ -1,56 +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 NUnit.Framework;
-using osu.Game.Beatmaps;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Osu.Mods;
-
-namespace osu.Game.Tests.Beatmaps
-{
- [TestFixture]
- public class BeatmapDifficultyCacheTest
- {
- [Test]
- public void TestKeyEqualsWithDifferentModInstances()
- {
- var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
-
- Assert.That(key1, Is.EqualTo(key2));
- }
-
- [Test]
- public void TestKeyEqualsWithDifferentModOrder()
- {
- var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
-
- Assert.That(key1, Is.EqualTo(key2));
- }
-
- [TestCase(1.3, DifficultyRating.Easy)]
- [TestCase(1.993, DifficultyRating.Easy)]
- [TestCase(1.998, DifficultyRating.Normal)]
- [TestCase(2.4, DifficultyRating.Normal)]
- [TestCase(2.693, DifficultyRating.Normal)]
- [TestCase(2.698, DifficultyRating.Hard)]
- [TestCase(3.5, DifficultyRating.Hard)]
- [TestCase(3.993, DifficultyRating.Hard)]
- [TestCase(3.997, DifficultyRating.Insane)]
- [TestCase(5.0, DifficultyRating.Insane)]
- [TestCase(5.292, DifficultyRating.Insane)]
- [TestCase(5.297, DifficultyRating.Expert)]
- [TestCase(6.2, DifficultyRating.Expert)]
- [TestCase(6.493, DifficultyRating.Expert)]
- [TestCase(6.498, DifficultyRating.ExpertPlus)]
- [TestCase(8.3, DifficultyRating.ExpertPlus)]
- public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
- {
- var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
-
- Assert.AreEqual(expectedBracket, actualBracket);
- }
- }
-}
diff --git a/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs
new file mode 100644
index 0000000000..da457c9e8f
--- /dev/null
+++ b/osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs
@@ -0,0 +1,146 @@
+// 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 System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Tests.Beatmaps.IO;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Tests.Beatmaps
+{
+ [HeadlessTest]
+ public class TestSceneBeatmapDifficultyCache : OsuTestScene
+ {
+ public const double BASE_STARS = 5.55;
+
+ private BeatmapSetInfo importedSet;
+
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
+ private TestBeatmapDifficultyCache difficultyCache;
+
+ private IBindable starDifficultyBindable;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osu)
+ {
+ importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).Result;
+ }
+
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("setup difficulty cache", () =>
+ {
+ SelectedMods.Value = Array.Empty();
+
+ Child = difficultyCache = new TestBeatmapDifficultyCache();
+
+ starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First());
+ });
+
+ AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS);
+ }
+
+ [Test]
+ public void TestStarDifficultyChangesOnModSettings()
+ {
+ OsuModDoubleTime dt = null;
+
+ AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5);
+
+ AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25);
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25);
+
+ AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } });
+ AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75);
+ }
+
+ [Test]
+ public void TestKeyEqualsWithDifferentModInstances()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyEqualsWithDifferentModOrder()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyDoesntEqualWithDifferentModSettings()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
+
+ Assert.That(key1, Is.Not.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
+ }
+
+ [Test]
+ public void TestKeyEqualWithMatchingModSettings()
+ {
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
+
+ Assert.That(key1, Is.EqualTo(key2));
+ Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
+ }
+
+ [TestCase(1.3, DifficultyRating.Easy)]
+ [TestCase(1.993, DifficultyRating.Easy)]
+ [TestCase(1.998, DifficultyRating.Normal)]
+ [TestCase(2.4, DifficultyRating.Normal)]
+ [TestCase(2.693, DifficultyRating.Normal)]
+ [TestCase(2.698, DifficultyRating.Hard)]
+ [TestCase(3.5, DifficultyRating.Hard)]
+ [TestCase(3.993, DifficultyRating.Hard)]
+ [TestCase(3.997, DifficultyRating.Insane)]
+ [TestCase(5.0, DifficultyRating.Insane)]
+ [TestCase(5.292, DifficultyRating.Insane)]
+ [TestCase(5.297, DifficultyRating.Expert)]
+ [TestCase(6.2, DifficultyRating.Expert)]
+ [TestCase(6.493, DifficultyRating.Expert)]
+ [TestCase(6.498, DifficultyRating.ExpertPlus)]
+ [TestCase(8.3, DifficultyRating.ExpertPlus)]
+ public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
+ {
+ var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
+
+ Assert.AreEqual(expectedBracket, actualBracket);
+ }
+
+ private class TestBeatmapDifficultyCache : BeatmapDifficultyCache
+ {
+ protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
+ {
+ var rateAdjust = lookup.OrderedMods.OfType().SingleOrDefault();
+ if (rateAdjust != null)
+ return Task.FromResult(new StarDifficulty(BASE_STARS + rateAdjust.SpeedChange.Value, 0));
+
+ return Task.FromResult(new StarDifficulty(BASE_STARS, 0));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
index 8124bd4199..bab8dfc983 100644
--- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
+++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs
@@ -50,10 +50,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(1));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1));
// the first should be overwritten by the second import.
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
@@ -76,10 +76,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
@@ -101,10 +101,10 @@ namespace osu.Game.Tests.Skins.IO
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Count, Is.EqualTo(2));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
- Assert.That(osu.Dependencies.Get().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
+ Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
index 3017428039..290ba3317b 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{
+ private OsuConfigManager localConfig;
+
private HUDOverlay hudOverlay;
[Cached]
@@ -31,8 +33,14 @@ namespace osu.Game.Tests.Visual.Gameplay
private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType>().First();
- [Resolved]
- private OsuConfigManager config { get; set; }
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
+ }
+
+ [SetUp]
+ public void SetUp() => Schedule(() => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always));
[Test]
public void TestComboCounterIncrementing()
@@ -85,11 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
createNew();
- HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
-
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
- AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
+ AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
@@ -98,37 +102,28 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
-
- AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
}
[Test]
public void TestExternalHideDoesntAffectConfig()
{
- HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
-
createNew();
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
- AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode));
+ AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault);
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
- AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.HUDVisibilityMode));
+ AddAssert("config unchanged", () => localConfig.GetBindable(OsuSetting.HUDVisibilityMode).IsDefault);
}
[Test]
public void TestChangeHUDVisibilityOnHiddenKeyCounter()
{
- bool keyCounterVisibleValue = false;
-
createNew();
- AddStep("save keycounter visible value", () => keyCounterVisibleValue = config.Get(OsuSetting.KeyOverlay));
- AddStep("set keycounter visible false", () =>
+ AddStep("hide key overlay", () =>
{
- config.SetValue(OsuSetting.KeyOverlay, false);
+ localConfig.SetValue(OsuSetting.KeyOverlay, false);
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
});
@@ -139,24 +134,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
-
- AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
}
[Test]
public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad()
{
- HUDVisibilityMode originalConfigValue = default;
-
- AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.HUDVisibilityMode));
-
- AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
+ AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
createNew();
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType().Single().ComponentsLoaded);
-
- AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
}
private void createNew(Action action = null)
@@ -175,5 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay
Child = hudOverlay;
});
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ localConfig?.Dispose();
+ base.Dispose(isDisposing);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
index 299bbacf08..8ca578b592 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs
@@ -10,11 +10,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
@@ -108,12 +108,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
EndDate = { Value = DateTimeOffset.Now },
}),
createDrawableRoom(new Room
- {
- Name = { Value = "Room 4 (realtime)" },
- Status = { Value = new RoomStatusOpen() },
- Category = { Value = RoomCategory.Realtime },
- }),
- createDrawableRoom(new Room
{
Name = { Value = "Room 4 (spotlight)" },
Status = { Value = new RoomStatusOpen() },
@@ -134,7 +128,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Name = { Value = "Room with password" },
Status = { Value = new RoomStatusOpen() },
- Category = { Value = RoomCategory.Realtime },
+ Type = { Value = MatchType.HeadToHead },
}));
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType().Single().Alpha));
@@ -159,10 +153,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
}));
}
- var drawableRoom = new DrawableRoom(room) { MatchingFilter = true };
- drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected;
-
- return drawableRoom;
+ return new DrawableLoungeRoom(room) { MatchingFilter = true };
}
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index 7e822f898e..2f895d2157 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -9,6 +9,7 @@ using osu.Framework.Testing;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Osu;
+using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
@@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
private RoomsContainer container;
@@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("add rooms", () => RoomManager.AddRooms(3));
AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
- AddStep("remove first room", () => RoomManager.Rooms.Remove(RoomManager.Rooms.FirstOrDefault()));
+ AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault()));
AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
@@ -150,6 +151,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
private Room getRoomInFlow(int index) =>
- (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
+ (container.ChildrenOfType>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
}
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
deleted file mode 100644
index 71ba5db481..0000000000
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchHeader.cs
+++ /dev/null
@@ -1,55 +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 NUnit.Framework;
-using osu.Game.Beatmaps;
-using osu.Game.Online.Rooms;
-using osu.Game.Rulesets.Osu;
-using osu.Game.Rulesets.Osu.Mods;
-using osu.Game.Screens.OnlinePlay.Match.Components;
-using osu.Game.Tests.Visual.OnlinePlay;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Multiplayer
-{
- public class TestSceneMatchHeader : OnlinePlayTestScene
- {
- [SetUp]
- public new void Setup() => Schedule(() =>
- {
- SelectedRoom.Value = new Room
- {
- Name = { Value = "A very awesome room" },
- Host = { Value = new User { Id = 2, Username = "peppy" } },
- Playlist =
- {
- new PlaylistItem
- {
- Beatmap =
- {
- Value = new BeatmapInfo
- {
- Metadata = new BeatmapMetadata
- {
- Title = "Title",
- Artist = "Artist",
- AuthorString = "Author",
- },
- Version = "Version",
- Ruleset = new OsuRuleset().RulesetInfo
- }
- },
- RequiredMods =
- {
- new OsuModDoubleTime(),
- new OsuModNoFail(),
- new OsuModRelax(),
- }
- }
- }
- };
-
- Child = new Header();
- });
- }
-}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
index e618b28f40..6c1aed71e6 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
@@ -236,8 +236,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter));
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
index c66d5429d6..99f6ab1ae1 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs
@@ -9,7 +9,6 @@ using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge;
-using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
@@ -18,7 +17,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen;
@@ -59,20 +58,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
+ AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType().Any());
AddStep("exit screen", () => Stack.Exit());
- AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
+ AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType().Any());
}
[Test]
public void TestJoinRoomWithPassword()
{
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick());
@@ -83,12 +82,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestJoinRoomWithPasswordViaKeyboardOnly()
{
- DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
+ DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
- AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
+ AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "password");
AddStep("press enter", () => InputManager.Key(Key.Enter));
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
index ea10fc1b8b..21364fe154 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs
@@ -59,23 +59,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
}
- [Test]
- public void TestSettingValidity()
- {
- AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value);
-
- AddStep("set playlist", () =>
- {
- SelectedRoom.Value.Playlist.Add(new PlaylistItem
- {
- Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
- Ruleset = { Value = new OsuRuleset().RulesetInfo },
- });
- });
-
- AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value);
- }
-
[Test]
public void TestCreatedRoom()
{
@@ -97,6 +80,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => Client.Room != null);
}
+ [Test]
+ public void TestSettingValidity()
+ {
+ AddAssert("create button not enabled", () => !this.ChildrenOfType().Single().Enabled.Value);
+
+ AddStep("set playlist", () =>
+ {
+ SelectedRoom.Value.Playlist.Add(new PlaylistItem
+ {
+ Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
+ Ruleset = { Value = new OsuRuleset().RulesetInfo },
+ });
+ });
+
+ AddAssert("create button enabled", () => this.ChildrenOfType().Single().Enabled.Value);
+ }
+
[Test]
public void TestStartMatchWhileSpectating()
{
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
index aff0e7ba4b..a4bcb4baae 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
{
- protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
+ protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen;
@@ -37,6 +37,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
@@ -53,6 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists
public void TestScrollSelectedIntoView()
{
AddStep("add rooms", () => RoomManager.AddRooms(30));
+ AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
index 98882b659c..04b44efd32 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs
@@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent);
}
- private class TestRoomSettings : PlaylistsMatchSettingsOverlay
+ private class TestRoomSettings : PlaylistsRoomSettingsOverlay
{
public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton;
diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
index 9fc29049ef..9051c71fc6 100644
--- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
+++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
@@ -11,7 +11,6 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
-using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
@@ -19,7 +18,6 @@ using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay;
-using osu.Game.Users;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Playlists
@@ -36,18 +34,6 @@ namespace osu.Game.Tests.Visual.Playlists
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
-
- ((DummyAPIAccess)API).HandleRequest = req =>
- {
- switch (req)
- {
- case CreateRoomScoreRequest createRoomScoreRequest:
- createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
- return true;
- }
-
- return false;
- };
}
[SetUpSteps]
@@ -66,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
SelectedRoom.Value.RoomID.Value = 1;
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
SelectedRoom.Value.Playlist.Add(new PlaylistItem
@@ -86,7 +72,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("set room properties", () =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
@@ -96,7 +82,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("move mouse to create button", () =>
{
- InputManager.MoveMouseTo(this.ChildrenOfType().Single());
+ InputManager.MoveMouseTo(this.ChildrenOfType().Single());
});
AddStep("click", () => InputManager.Click(MouseButton.Left));
@@ -137,7 +123,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("load room", () =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
- SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
+ SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedSet.Beatmaps[0] },
@@ -147,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create room", () =>
{
- InputManager.MoveMouseTo(match.ChildrenOfType().Single());
+ InputManager.MoveMouseTo(match.ChildrenOfType().Single());
InputManager.Click(MouseButton.Left);
});
diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
deleted file mode 100644
index 566452249f..0000000000
--- a/osu.Game.Tests/Visual/Ranking/TestSceneStarRatingDisplay.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System.Linq;
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Utils;
-using osu.Game.Beatmaps;
-using osu.Game.Screens.Ranking.Expanded;
-using osuTK;
-
-namespace osu.Game.Tests.Visual.Ranking
-{
- public class TestSceneStarRatingDisplay : OsuTestScene
- {
- [Test]
- public void TestDisplay()
- {
- AddStep("load displays", () => Child = new FillFlowContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- ChildrenEnumerable = new[]
- {
- 1.23,
- 2.34,
- 3.45,
- 4.56,
- 5.67,
- 6.78,
- 10.11,
- }.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0))
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre
- })
- });
- }
-
- [Test]
- public void TestChangingStarRatingDisplay()
- {
- StarRatingDisplay starRating = null;
-
- AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(3f),
- });
-
- AddRepeatStep("set random value", () =>
- {
- starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
- }, 10);
-
- AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
- {
- if (starRating != null)
- starRating.Current.Value = new StarDifficulty(d, 1);
- });
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
index fa2c9ecdea..57ba051214 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
[SetUpSteps]
public void SetUpSteps()
{
+ AddUntilStep("wait for load", () => panel.ChildrenOfType().Any());
AddStep("Scroll to top", () => panel.ChildrenOfType().First().ScrollToTop());
AddWaitStep("wait for scroll", 5);
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
index df59b9284b..d9cce69ee3 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs
@@ -76,5 +76,23 @@ namespace osu.Game.Tests.Visual.Settings
AddStep("restore default", () => sliderBar.Current.SetDefault());
AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
}
+
+ [Test]
+ public void TestWarningTextVisibility()
+ {
+ SettingsNumberBox numberBox = null;
+
+ AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox());
+ AddAssert("warning text not created", () => !numberBox.ChildrenOfType().Any());
+
+ AddStep("set warning text", () => numberBox.WarningText = "this is a warning!");
+ AddAssert("warning text created", () => numberBox.ChildrenOfType().Single().Alpha == 1);
+
+ AddStep("unset warning text", () => numberBox.WarningText = default);
+ AddAssert("warning text hidden", () => numberBox.ChildrenOfType().Single().Alpha == 0);
+
+ AddStep("set warning text again", () => numberBox.WarningText = "another warning!");
+ AddAssert("warning text shown again", () => numberBox.ChildrenOfType().Single().Alpha == 1);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
index 115d2fec7d..0af77b3b5a 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs
@@ -4,6 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Settings
@@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture]
public class TestSceneSettingsPanel : OsuTestScene
{
- private readonly SettingsPanel settings;
- private readonly DialogOverlay dialogOverlay;
+ private SettingsPanel settings;
+ private DialogOverlay dialogOverlay;
- public TestSceneSettingsPanel()
+ [SetUpSteps]
+ public void SetUpSteps()
{
- settings = new SettingsOverlay
+ AddStep("create settings", () =>
{
- State = { Value = Visibility.Visible }
- };
- Add(dialogOverlay = new DialogOverlay
- {
- Depth = -1
+ settings?.Expire();
+
+ Add(settings = new SettingsOverlay
+ {
+ State = { Value = Visibility.Visible }
+ });
});
}
+ [Test]
+ public void ToggleVisibility()
+ {
+ AddWaitStep("wait some", 5);
+ AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
+ }
+
[BackgroundDependencyLoader]
private void load()
{
- Dependencies.Cache(dialogOverlay);
+ Add(dialogOverlay = new DialogOverlay
+ {
+ Depth = -1
+ });
- Add(settings);
+ Dependencies.Cache(dialogOverlay);
}
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
index a416fd4daf..2b38c4f936 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
@@ -65,6 +66,12 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("show", () => { infoWedge.Show(); });
+ AddSliderStep("change star difficulty", 0, 11.9, 5.55, v =>
+ {
+ foreach (var hasCurrentValue in infoWedge.Info.ChildrenOfType>())
+ hasCurrentValue.Current.Value = new StarDifficulty(v, 0);
+ });
+
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
var instance = rulesetInfo.CreateInstance();
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
new file mode 100644
index 0000000000..2806e6d347
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneStarRatingDisplay.cs
@@ -0,0 +1,71 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public class TestSceneStarRatingDisplay : OsuTestScene
+ {
+ [TestCase(StarRatingDisplaySize.Regular)]
+ [TestCase(StarRatingDisplaySize.Small)]
+ public void TestDisplay(StarRatingDisplaySize size)
+ {
+ AddStep("load displays", () =>
+ {
+ Child = new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(2f),
+ Direction = FillDirection.Horizontal,
+ ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(2f),
+ Direction = FillDirection.Vertical,
+ ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }),
+ })
+ };
+ });
+ }
+
+ [Test]
+ public void TestSpectrum()
+ {
+ StarRatingDisplay starRating = null;
+
+ AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), animated: true)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(3f),
+ });
+
+ AddRepeatStep("set random value", () =>
+ {
+ starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
+ }, 10);
+
+ AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
+ {
+ if (starRating != null)
+ starRating.Current.Value = new StarDifficulty(d, 1);
+ });
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
index 6ed623d0c0..5025e4ad4b 100644
--- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -14,6 +14,7 @@ using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Framework.Utils;
+using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@@ -56,12 +57,28 @@ namespace osu.Game.Beatmaps
[Resolved]
private Bindable> currentMods { get; set; }
+ private ModSettingChangeTracker modSettingChangeTracker;
+ private ScheduledDelegate debouncedModSettingsChange;
+
protected override void LoadComplete()
{
base.LoadComplete();
currentRuleset.BindValueChanged(_ => updateTrackedBindables());
- currentMods.BindValueChanged(_ => updateTrackedBindables(), true);
+
+ currentMods.BindValueChanged(mods =>
+ {
+ modSettingChangeTracker?.Dispose();
+
+ updateTrackedBindables();
+
+ modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
+ modSettingChangeTracker.SettingChanged += _ =>
+ {
+ debouncedModSettingsChange?.Cancel();
+ debouncedModSettingsChange = Scheduler.AddDelayed(updateTrackedBindables, 100);
+ };
+ }, true);
}
///
@@ -84,7 +101,7 @@ namespace osu.Game.Beatmaps
/// Retrieves a bindable containing the star difficulty of a with a given and combination.
///
///
- /// The bindable will not update to follow the currently-selected ruleset and mods.
+ /// The bindable will not update to follow the currently-selected ruleset and mods or its settings.
///
/// The to get the difficulty of.
/// The to get the difficulty with. If null, the 's ruleset is used.
@@ -275,6 +292,8 @@ namespace osu.Game.Beatmaps
{
base.Dispose(isDisposing);
+ modSettingChangeTracker?.Dispose();
+
cancelTrackedBindableUpdate();
updateScheduler?.Dispose();
}
@@ -297,7 +316,7 @@ namespace osu.Game.Beatmaps
public bool Equals(DifficultyCacheLookup other)
=> Beatmap.ID == other.Beatmap.ID
&& Ruleset.ID == other.Ruleset.ID
- && OrderedMods.Select(m => m.Acronym).SequenceEqual(other.OrderedMods.Select(m => m.Acronym));
+ && OrderedMods.SequenceEqual(other.OrderedMods);
public override int GetHashCode()
{
@@ -307,7 +326,7 @@ namespace osu.Game.Beatmaps
hashCode.Add(Ruleset.ID);
foreach (var mod in OrderedMods)
- hashCode.Add(mod.Acronym);
+ hashCode.Add(mod);
return hashCode.ToHashCode();
}
diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
new file mode 100644
index 0000000000..c239fda455
--- /dev/null
+++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs
@@ -0,0 +1,168 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Localisation;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Beatmaps.Drawables
+{
+ ///
+ /// A pill that displays the star rating of a beatmap.
+ ///
+ public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue
+ {
+ private readonly bool animated;
+ private readonly Box background;
+ private readonly SpriteIcon starIcon;
+ private readonly OsuSpriteText starsText;
+
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
+
+ public Bindable Current
+ {
+ get => current.Current;
+ set => current.Current = value;
+ }
+
+ private readonly Bindable displayedStars = new BindableDouble();
+
+ ///
+ /// The currently displayed stars of this display wrapped in a bindable.
+ /// This bindable gets transformed on change rather than instantaneous, if animation is enabled.
+ ///
+ public IBindable DisplayedStars => displayedStars;
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ [Resolved(canBeNull: true)]
+ private OverlayColourProvider colourProvider { get; set; }
+
+ ///
+ /// Creates a new using an already computed .
+ ///
+ /// The already computed to display.
+ /// The size of the star rating display.
+ /// Whether the star rating display will perform transforms on change rather than updating instantaneously.
+ public StarRatingDisplay(StarDifficulty starDifficulty, StarRatingDisplaySize size = StarRatingDisplaySize.Regular, bool animated = false)
+ {
+ this.animated = animated;
+
+ Current.Value = starDifficulty;
+
+ AutoSizeAxes = Axes.Both;
+
+ MarginPadding margin = default;
+
+ switch (size)
+ {
+ case StarRatingDisplaySize.Small:
+ margin = new MarginPadding { Horizontal = 7f };
+ break;
+
+ case StarRatingDisplaySize.Range:
+ margin = new MarginPadding { Horizontal = 8f };
+ break;
+
+ case StarRatingDisplaySize.Regular:
+ margin = new MarginPadding { Horizontal = 8f, Vertical = 2f };
+ break;
+ }
+
+ InternalChild = new CircularContainer
+ {
+ Masking = true,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new GridContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Margin = margin,
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.Absolute, 3f),
+ new Dimension(GridSizeMode.AutoSize, minSize: 25f),
+ },
+ RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
+ Content = new[]
+ {
+ new[]
+ {
+ starIcon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.Solid.Star,
+ Size = new Vector2(8f),
+ },
+ Empty(),
+ starsText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Margin = new MarginPadding { Bottom = 1.5f },
+ // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f
+ // see https://github.com/ppy/osu-framework/issues/3271.
+ Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold),
+ Shadow = false,
+ },
+ }
+ }
+ },
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Current.BindValueChanged(c =>
+ {
+ if (animated)
+ this.TransformBindableTo(displayedStars, c.NewValue.Stars, 750, Easing.OutQuint);
+ else
+ displayedStars.Value = c.NewValue.Stars;
+ });
+
+ displayedStars.Value = Current.Value.Stars;
+
+ displayedStars.BindValueChanged(s =>
+ {
+ starsText.Text = s.NewValue.ToLocalisableString("0.00");
+
+ background.Colour = colours.ForStarDifficulty(s.NewValue);
+
+ starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47");
+ starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f);
+ }, true);
+ }
+ }
+
+ public enum StarRatingDisplaySize
+ {
+ Small,
+ Range,
+ Regular,
+ }
+}
diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs
index c9d0c4bc41..b0feb7bb78 100644
--- a/osu.Game/Database/MutableDatabaseBackedStore.cs
+++ b/osu.Game/Database/MutableDatabaseBackedStore.cs
@@ -36,6 +36,11 @@ namespace osu.Game.Database
///
public IQueryable ConsumableItems => AddIncludesForConsumption(ContextFactory.Get().Set());
+ ///
+ /// Access barebones items with no includes.
+ ///
+ public IQueryable Items => ContextFactory.Get().Set();
+
///
/// Add a to the database.
///
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index 2705caccff..ed9f0710b0 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -22,7 +22,10 @@ namespace osu.Game.Graphics.UserInterface
public void TakeFocus()
{
- if (allowImmediateFocus) GetContainingInputManager().ChangeFocus(this);
+ if (!allowImmediateFocus)
+ return;
+
+ Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this), false);
}
public new void KillFocus() => base.KillFocus();
diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs
index 8d686e8c2f..cbf3e6b3aa 100644
--- a/osu.Game/Graphics/UserInterface/Nub.cs
+++ b/osu.Game/Graphics/UserInterface/Nub.cs
@@ -6,6 +6,7 @@ using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
@@ -57,18 +58,13 @@ namespace osu.Game.Graphics.UserInterface
EdgeEffect = new EdgeEffectParameters
{
- Colour = GlowColour,
+ Colour = GlowColour.Opacity(0),
Type = EdgeEffectType.Glow,
Radius = 10,
Roundness = 8,
};
}
- protected override void LoadComplete()
- {
- FadeEdgeEffectTo(0);
- }
-
private bool glowing;
public bool Glowing
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index cd9ca9f87f..82a3e73b84 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -36,6 +36,7 @@ namespace osu.Game.Graphics.UserInterface
public Color4 BackgroundColour
{
+ get => backgroundColour ?? Color4.White;
set
{
backgroundColour = value;
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index 61dd5fb2d9..42f628a75a 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -69,6 +69,7 @@ namespace osu.Game.Graphics.UserInterface
BackgroundColour = Color4.Black.Opacity(0.5f);
MaskingContainer.CornerRadius = corner_radius;
+ Alpha = 0;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
ItemsContainer.Padding = new MarginPadding(5);
@@ -94,9 +95,11 @@ namespace osu.Game.Graphics.UserInterface
protected override void AnimateClose()
{
- this.FadeOut(300, Easing.OutQuint);
if (wasOpened)
+ {
+ this.FadeOut(300, Easing.OutQuint);
sampleClose?.Play();
+ }
}
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
diff --git a/osu.Game/Online/Rooms/RoomCategory.cs b/osu.Game/Online/Rooms/RoomCategory.cs
index bb9f1298d3..a1dcfa5fd9 100644
--- a/osu.Game/Online/Rooms/RoomCategory.cs
+++ b/osu.Game/Online/Rooms/RoomCategory.cs
@@ -8,6 +8,5 @@ namespace osu.Game.Online.Rooms
// used for osu-web deserialization so names shouldn't be changed.
Normal,
Spotlight,
- Realtime,
}
}
diff --git a/osu.Game/Overlays/RestoreDefaultValueButton.cs b/osu.Game/Overlays/RestoreDefaultValueButton.cs
index fd3ee16fe6..87a294cc10 100644
--- a/osu.Game/Overlays/RestoreDefaultValueButton.cs
+++ b/osu.Game/Overlays/RestoreDefaultValueButton.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
@@ -38,12 +39,12 @@ namespace osu.Game.Overlays
current.ValueChanged += _ => UpdateState();
current.DefaultChanged += _ => UpdateState();
current.DisabledChanged += _ => UpdateState();
- UpdateState();
+
+ if (IsLoaded)
+ UpdateState();
}
}
- private Color4 buttonColour;
-
private bool hovering;
public RestoreDefaultValueButton()
@@ -58,12 +59,11 @@ namespace osu.Game.Overlays
private void load(OsuColour colour)
{
BackgroundColour = colour.Yellow;
- buttonColour = colour.Yellow;
Content.Width = 0.33f;
Content.CornerRadius = 3;
Content.EdgeEffect = new EdgeEffectParameters
{
- Colour = buttonColour.Opacity(0.1f),
+ Colour = BackgroundColour.Opacity(0.1f),
Type = EdgeEffectType.Glow,
Radius = 2,
};
@@ -81,7 +81,10 @@ namespace osu.Game.Overlays
protected override void LoadComplete()
{
base.LoadComplete();
- UpdateState();
+
+ // avoid unnecessary transforms on first display.
+ Alpha = currentAlpha;
+ Background.Colour = currentColour;
}
public LocalisableString TooltipText => "revert to default";
@@ -101,14 +104,16 @@ namespace osu.Game.Overlays
public void UpdateState() => Scheduler.AddOnce(updateState);
+ private float currentAlpha => current.IsDefault ? 0f : hovering && !current.Disabled ? 1f : 0.65f;
+ private ColourInfo currentColour => current.Disabled ? Color4.Gray : BackgroundColour;
+
private void updateState()
{
if (current == null)
return;
- this.FadeTo(current.IsDefault ? 0f :
- hovering && !current.Disabled ? 1f : 0.65f, 200, Easing.OutQuint);
- this.FadeColour(current.Disabled ? Color4.Gray : buttonColour, 200, Easing.OutQuint);
+ this.FadeTo(currentAlpha, 200, Easing.OutQuint);
+ Background.FadeColour(currentColour, 200, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
index 501f1b86b8..d697b45424 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs
@@ -21,17 +21,26 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
private SettingsDropdown dropdown;
- protected override void Dispose(bool isDisposing)
+ [BackgroundDependencyLoader]
+ private void load()
{
- base.Dispose(isDisposing);
-
- if (audio != null)
+ Children = new Drawable[]
{
- audio.OnNewDevice -= onDeviceChanged;
- audio.OnLostDevice -= onDeviceChanged;
- }
+ dropdown = new AudioDeviceSettingsDropdown
+ {
+ Keywords = new[] { "speaker", "headphone", "output" }
+ }
+ };
+
+ updateItems();
+
+ audio.OnNewDevice += onDeviceChanged;
+ audio.OnLostDevice += onDeviceChanged;
+ dropdown.Current = audio.AudioDevice;
}
+ private void onDeviceChanged(string name) => updateItems();
+
private void updateItems()
{
var deviceItems = new List { string.Empty };
@@ -50,26 +59,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
dropdown.Items = deviceItems.Distinct().ToList();
}
- private void onDeviceChanged(string name) => updateItems();
-
- protected override void LoadComplete()
+ protected override void Dispose(bool isDisposing)
{
- base.LoadComplete();
+ base.Dispose(isDisposing);
- Children = new Drawable[]
+ if (audio != null)
{
- dropdown = new AudioDeviceSettingsDropdown
- {
- Keywords = new[] { "speaker", "headphone", "output" }
- }
- };
-
- updateItems();
-
- dropdown.Current = audio.AudioDevice;
-
- audio.OnNewDevice += onDeviceChanged;
- audio.OnLostDevice += onDeviceChanged;
+ audio.OnNewDevice -= onDeviceChanged;
+ audio.OnLostDevice -= onDeviceChanged;
+ }
}
private class AudioDeviceSettingsDropdown : SettingsDropdown
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
index 124b3b804c..adf1453d1a 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs
@@ -98,8 +98,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
- AutoSizeDuration = transition_duration,
- AutoSizeEasing = Easing.OutQuint,
Masking = true,
Children = new[]
{
@@ -176,13 +174,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
scalingMode.BindValueChanged(mode =>
{
scalingSettings.ClearTransforms();
- scalingSettings.AutoSizeAxes = mode.NewValue != ScalingMode.Off ? Axes.Y : Axes.None;
+ scalingSettings.AutoSizeDuration = transition_duration;
+ scalingSettings.AutoSizeEasing = Easing.OutQuint;
- if (mode.NewValue == ScalingMode.Off)
- scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
+ updateScalingModeVisibility();
+ });
- scalingSettings.ForEach(s => s.TransferValueOnCommit = mode.NewValue == ScalingMode.Everything);
- }, true);
+ // initial update bypasses transforms
+ updateScalingModeVisibility();
void updateResolutionDropdown()
{
@@ -191,6 +190,15 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
else
resolutionDropdown.Hide();
}
+
+ void updateScalingModeVisibility()
+ {
+ if (scalingMode.Value == ScalingMode.Off)
+ scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
+
+ scalingSettings.AutoSizeAxes = scalingMode.Value != ScalingMode.Off ? Axes.Y : Axes.None;
+ scalingSettings.ForEach(s => s.TransferValueOnCommit = scalingMode.Value == ScalingMode.Everything);
+ }
}
private void bindPreviewEvent(Bindable bindable)
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index ef2027fdab..5282217013 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Settings
{
set
{
- bool hasValue = string.IsNullOrWhiteSpace(value.ToString());
+ bool hasValue = !string.IsNullOrWhiteSpace(value.ToString());
if (warningText == null)
{
@@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Settings
FlowContent.Add(warningText = new SettingsNoticeText(colours) { Margin = new MarginPadding { Bottom = 5 } });
}
- warningText.Alpha = hasValue ? 0 : 1;
+ warningText.Alpha = hasValue ? 1 : 0;
warningText.Text = value.ToString(); // TODO: Remove ToString() call after TextFlowContainer supports localisation (see https://github.com/ppy/osu-framework/issues/4636).
}
}
@@ -93,15 +93,13 @@ namespace osu.Game.Overlays.Settings
public bool MatchingFilter
{
- set => this.FadeTo(value ? 1 : 0);
+ set => Alpha = value ? 1 : 0;
}
public bool FilteringActive { get; set; }
public event Action SettingChanged;
- private readonly RestoreDefaultValueButton restoreDefaultButton;
-
protected SettingsItem()
{
RelativeSizeAxes = Axes.X;
@@ -110,7 +108,6 @@ namespace osu.Game.Overlays.Settings
InternalChildren = new Drawable[]
{
- restoreDefaultButton = new RestoreDefaultValueButton(),
FlowContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
@@ -123,7 +120,7 @@ namespace osu.Game.Overlays.Settings
},
};
- // all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
+ // IMPORTANT: all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
// never loaded, but requires bindable storage.
if (controlWithCurrent == null)
throw new ArgumentException(@$"Control created via {nameof(CreateControl)} must implement {nameof(IHasCurrentValue)}");
@@ -132,12 +129,17 @@ namespace osu.Game.Overlays.Settings
controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
}
- protected override void LoadComplete()
+ [BackgroundDependencyLoader]
+ private void load()
{
- base.LoadComplete();
-
+ // intentionally done before LoadComplete to avoid overhead.
if (ShowsDefaultIndicator)
- restoreDefaultButton.Current = controlWithCurrent.Current;
+ {
+ AddInternal(new RestoreDefaultValueButton
+ {
+ Current = controlWithCurrent.Current,
+ });
+ }
}
private void updateDisabled()
diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs
index 30a53b351d..cf6a313a1f 100644
--- a/osu.Game/Overlays/Settings/SidebarButton.cs
+++ b/osu.Game/Overlays/Settings/SidebarButton.cs
@@ -22,6 +22,9 @@ namespace osu.Game.Overlays.Settings
private readonly Box selectionIndicator;
private readonly Container text;
+ // always consider as part of flow, even when not visible (for the sake of the initial animation).
+ public override bool IsPresent => true;
+
private SettingsSection section;
public SettingsSection Section
diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs
index f1c41c4b50..376e36ea4e 100644
--- a/osu.Game/Overlays/SettingsPanel.cs
+++ b/osu.Game/Overlays/SettingsPanel.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@@ -58,6 +59,12 @@ namespace osu.Game.Overlays
private readonly bool showSidebar;
+ private LoadingLayer loading;
+
+ private readonly List loadableSections = new List();
+
+ private Task sectionsLoadingTask;
+
protected SettingsPanel(bool showSidebar)
{
this.showSidebar = showSidebar;
@@ -86,69 +93,48 @@ namespace osu.Game.Overlays
Colour = OsuColour.Gray(0.05f),
Alpha = 1,
},
- SectionsContainer = new SettingsSectionsContainer
+ loading = new LoadingLayer
{
- Masking = true,
- RelativeSizeAxes = Axes.Both,
- ExpandableHeader = CreateHeader(),
- FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
- {
- RelativeSizeAxes = Axes.X,
- Origin = Anchor.TopCentre,
- Anchor = Anchor.TopCentre,
- Width = 0.95f,
- Margin = new MarginPadding
- {
- Top = 20,
- Bottom = 20
- },
- },
- Footer = CreateFooter()
- },
+ State = { Value = Visibility.Visible }
+ }
}
};
+ Add(SectionsContainer = new SettingsSectionsContainer
+ {
+ Masking = true,
+ RelativeSizeAxes = Axes.Both,
+ ExpandableHeader = CreateHeader(),
+ FixedHeader = searchTextBox = new SeekLimitedSearchTextBox
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Width = 0.95f,
+ Margin = new MarginPadding
+ {
+ Top = 20,
+ Bottom = 20
+ },
+ },
+ Footer = CreateFooter().With(f => f.Alpha = 0)
+ });
+
if (showSidebar)
{
AddInternal(Sidebar = new Sidebar { Width = sidebar_width });
-
- SectionsContainer.SelectedSection.ValueChanged += section =>
- {
- selectedSidebarButton.Selected = false;
- selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
- selectedSidebarButton.Selected = true;
- };
}
- searchTextBox.Current.ValueChanged += term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue;
-
CreateSections()?.ForEach(AddSection);
}
protected void AddSection(SettingsSection section)
{
- SectionsContainer.Add(section);
+ if (IsLoaded)
+ // just to keep things simple. can be accommodated for if we ever need it.
+ throw new InvalidOperationException("All sections must be added before the panel is loaded.");
- if (Sidebar != null)
- {
- var button = new SidebarButton
- {
- Section = section,
- Action = () =>
- {
- SectionsContainer.ScrollTo(section);
- Sidebar.State = ExpandedState.Contracted;
- },
- };
-
- Sidebar.Add(button);
-
- if (selectedSidebarButton == null)
- {
- selectedSidebarButton = Sidebar.Children.First();
- selectedSidebarButton.Selected = true;
- }
- }
+ loadableSections.Add(section);
}
protected virtual Drawable CreateHeader() => new Container();
@@ -161,6 +147,12 @@ namespace osu.Game.Overlays
ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint);
+ // delay load enough to ensure it doesn't overlap with the initial animation.
+ // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress.
+ // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining
+ // load complete overheads.
+ Scheduler.AddDelayed(loadSections, TRANSITION_LENGTH / 3);
+
Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
@@ -199,6 +191,78 @@ namespace osu.Game.Overlays
Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
+ private const double fade_in_duration = 1000;
+
+ private void loadSections()
+ {
+ if (sectionsLoadingTask != null)
+ return;
+
+ sectionsLoadingTask = LoadComponentsAsync(loadableSections, sections =>
+ {
+ SectionsContainer.AddRange(sections);
+ SectionsContainer.Footer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
+ SectionsContainer.SearchContainer.FadeInFromZero(fade_in_duration, Easing.OutQuint);
+
+ loading.Hide();
+
+ searchTextBox.Current.BindValueChanged(term => SectionsContainer.SearchContainer.SearchTerm = term.NewValue, true);
+ searchTextBox.TakeFocus();
+
+ loadSidebarButtons();
+ });
+ }
+
+ private void loadSidebarButtons()
+ {
+ if (Sidebar == null)
+ return;
+
+ LoadComponentsAsync(createSidebarButtons(), buttons =>
+ {
+ float delay = 0;
+
+ foreach (var button in buttons)
+ {
+ Sidebar.Add(button);
+
+ button.FadeOut()
+ .Delay(delay)
+ .FadeInFromZero(fade_in_duration, Easing.OutQuint);
+
+ delay += 40;
+ }
+
+ SectionsContainer.SelectedSection.BindValueChanged(section =>
+ {
+ if (selectedSidebarButton != null)
+ selectedSidebarButton.Selected = false;
+
+ selectedSidebarButton = Sidebar.Children.Single(b => b.Section == section.NewValue);
+ selectedSidebarButton.Selected = true;
+ }, true);
+ });
+ }
+
+ private IEnumerable createSidebarButtons()
+ {
+ foreach (var section in SectionsContainer)
+ {
+ yield return new SidebarButton
+ {
+ Section = section,
+ Action = () =>
+ {
+ if (!SectionsContainer.IsLoaded)
+ return;
+
+ SectionsContainer.ScrollTo(section);
+ Sidebar.State = ExpandedState.Contracted;
+ },
+ };
+ }
+ }
+
private class NonMaskedContent : Container
{
// masking breaks the pan-out transform with nested sub-settings panels.
diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
index 186514e868..3978378c3a 100644
--- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
+++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mods
{
// Intercept and extract the internal number bindable from DifficultyBindable.
// This will provide bounds and precision specifications for the slider bar.
- difficultyBindable = ((DifficultyBindable)value).GetBoundCopy();
+ difficultyBindable = (DifficultyBindable)value.GetBoundCopy();
sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber);
base.Current = difficultyBindable;
diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
index 664b88eef4..e4304795f2 100644
--- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs
+++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs
@@ -128,6 +128,6 @@ namespace osu.Game.Rulesets.Mods
ExtendedLimits.UnbindFrom(otherDifficultyBindable.ExtendedLimits);
}
- public new DifficultyBindable GetBoundCopy() => new DifficultyBindable { BindTarget = this };
+ protected override Bindable CreateInstance() => new DifficultyBindable();
}
}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index f2fd02c652..9f3b5eaf5b 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -129,6 +129,17 @@ namespace osu.Game.Rulesets.Mods
[JsonIgnore]
public virtual Type[] IncompatibleMods => Array.Empty();
+ private IReadOnlyList settingsBacking;
+
+ ///
+ /// A list of the all settings within this mod.
+ ///
+ internal IReadOnlyList Settings =>
+ settingsBacking ??= this.GetSettingsSourceProperties()
+ .Select(p => p.Item2.GetValue(this))
+ .Cast()
+ .ToList();
+
///
/// Creates a copy of this initialised to a default state.
///
@@ -191,15 +202,39 @@ namespace osu.Game.Rulesets.Mods
if (ReferenceEquals(this, other)) return true;
return GetType() == other.GetType() &&
- this.GetSettingsSourceProperties().All(pair =>
- EqualityComparer
-
+
@@ -93,7 +93,7 @@
-
+