diff --git a/osu.Android.props b/osu.Android.props
index 8a9bf1b9cd..05367c00f6 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
new file mode 100644
index 0000000000..a206aafb8a
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs
@@ -0,0 +1,46 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens.Edit.Setup;
+
+namespace osu.Game.Rulesets.Mania.Edit.Setup
+{
+ public class ManiaSetupSection : RulesetSetupSection
+ {
+ private LabelledSwitchButton specialStyle;
+
+ public ManiaSetupSection()
+ : base(new ManiaRuleset().RulesetInfo)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new Drawable[]
+ {
+ specialStyle = new LabelledSwitchButton
+ {
+ Label = "Use special (N+1) style",
+ Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.",
+ Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ specialStyle.Current.BindValueChanged(_ => updateBeatmap());
+ }
+
+ private void updateBeatmap()
+ {
+ Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index f4b6e10af4..1f79dae280 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -27,11 +27,13 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
+using osu.Game.Rulesets.Mania.Edit.Setup;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Scoring;
+using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Mania
@@ -390,6 +392,8 @@ namespace osu.Game.Rulesets.Mania
{
return new ManiaFilterCriteria();
}
+
+ public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();
}
public enum PlayfieldType
diff --git a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
new file mode 100644
index 0000000000..8cb778a2e1
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Screens.Edit.Setup;
+
+namespace osu.Game.Rulesets.Osu.Edit.Setup
+{
+ public class OsuSetupSection : RulesetSetupSection
+ {
+ private LabelledSliderBar stackLeniency;
+
+ public OsuSetupSection()
+ : base(new OsuRuleset().RulesetInfo)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Children = new[]
+ {
+ stackLeniency = new LabelledSliderBar
+ {
+ Label = "Stack Leniency",
+ Description = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.",
+ Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency)
+ {
+ Default = 0.7f,
+ MinValue = 0,
+ MaxValue = 1,
+ Precision = 0.1f
+ }
+ }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ stackLeniency.Current.BindValueChanged(_ => updateBeatmap());
+ }
+
+ private void updateBeatmap()
+ {
+ Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index b13cdff1ec..f4a93a571d 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -30,9 +30,11 @@ using osu.Game.Skinning;
using System;
using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
+using osu.Game.Rulesets.Osu.Edit.Setup;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.Statistics;
+using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Osu
@@ -305,5 +307,7 @@ namespace osu.Game.Rulesets.Osu
}
};
}
+
+ public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection();
}
}
diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
index 642ecf00b8..8be74f1a7c 100644
--- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
+++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs
@@ -11,6 +11,7 @@ using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.Input;
using osu.Game.Input.Bindings;
+using osu.Game.Rulesets;
using Realms;
namespace osu.Game.Tests.Database
@@ -42,7 +43,7 @@ namespace osu.Game.Tests.Database
KeyBindingContainer testContainer = new TestKeyBindingContainer();
- keyBindingStore.Register(testContainer);
+ keyBindingStore.Register(testContainer, Enumerable.Empty());
Assert.That(queryCount(), Is.EqualTo(3));
@@ -66,7 +67,7 @@ namespace osu.Game.Tests.Database
{
KeyBindingContainer testContainer = new TestKeyBindingContainer();
- keyBindingStore.Register(testContainer);
+ keyBindingStore.Register(testContainer, Enumerable.Empty());
using (var primaryUsage = realmContextFactory.GetForRead())
{
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
index 9253023c9a..c3c803ff23 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
@@ -4,8 +4,13 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
@@ -23,15 +28,31 @@ namespace osu.Game.Tests.Visual.Editing
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
}
- [BackgroundDependencyLoader]
- private void load()
- {
- Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+ [Test]
+ public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo);
- Child = new SetupScreen
+ [Test]
+ public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo);
+
+ [Test]
+ public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo);
+
+ [Test]
+ public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo);
+
+ private void runForRuleset(RulesetInfo rulesetInfo)
+ {
+ AddStep("create screen", () =>
{
- State = { Value = Visibility.Visible },
- };
+ editorBeatmap.BeatmapInfo.Ruleset = rulesetInfo;
+
+ Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+
+ Child = new SetupScreen
+ {
+ State = { Value = Visibility.Visible },
+ };
+ });
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
index 03d079261d..70271b0b08 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs
@@ -104,6 +104,9 @@ namespace osu.Game.Tests.Visual.Online
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg"
}, api.IsLoggedIn));
+ AddStep("Show ppy from username", () => profile.ShowUser(@"peppy"));
+ AddStep("Show flyte from username", () => profile.ShowUser(@"flyte"));
+
AddStep("Hide", profile.Hide);
AddStep("Show without reload", profile.Show);
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
index da474a64ba..997eac709d 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneTabletSettings.cs
@@ -1,14 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Linq;
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Handlers.Tablet;
-using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Overlays;
+using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections.Input;
using osuTK;
@@ -17,22 +18,34 @@ namespace osu.Game.Tests.Visual.Settings
[TestFixture]
public class TestSceneTabletSettings : OsuTestScene
{
- [BackgroundDependencyLoader]
- private void load(GameHost host)
- {
- var tabletHandler = new TestTabletHandler();
+ private TestTabletHandler tabletHandler;
+ private TabletSettings settings;
- AddRange(new Drawable[]
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("create settings", () =>
{
- new TabletSettings(tabletHandler)
+ tabletHandler = new TestTabletHandler();
+
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.None,
- Width = SettingsPanel.PANEL_WIDTH,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- }
+ settings = new TabletSettings(tabletHandler)
+ {
+ RelativeSizeAxes = Axes.None,
+ Width = SettingsPanel.PANEL_WIDTH,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }
+ };
});
+ AddStep("set square size", () => tabletHandler.SetTabletSize(new Vector2(100, 100)));
+ }
+
+ [Test]
+ public void TestVariousTabletSizes()
+ {
AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
AddStep("Test with square tablet", () => tabletHandler.SetTabletSize(new Vector2(300, 300)));
AddStep("Test with tall tablet", () => tabletHandler.SetTabletSize(new Vector2(100, 300)));
@@ -40,6 +53,71 @@ namespace osu.Game.Tests.Visual.Settings
AddStep("Test no tablet present", () => tabletHandler.SetTabletSize(Vector2.Zero));
}
+ [Test]
+ public void TestWideAspectRatioValidity()
+ {
+ AddStep("Test with wide tablet", () => tabletHandler.SetTabletSize(new Vector2(160, 100)));
+
+ AddStep("Reset to full area", () => settings.ChildrenOfType().First().TriggerClick());
+ ensureValid();
+
+ AddStep("rotate 10", () => tabletHandler.Rotation.Value = 10);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureInvalid();
+
+ AddStep("scale down", () => tabletHandler.AreaSize.Value *= 0.9f);
+ ensureValid();
+ }
+
+ [Test]
+ public void TestRotationValidity()
+ {
+ AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds);
+
+ AddStep("rotate 90", () => tabletHandler.Rotation.Value = 90);
+ ensureValid();
+
+ AddStep("rotate 180", () => tabletHandler.Rotation.Value = 180);
+
+ ensureValid();
+
+ AddStep("rotate 270", () => tabletHandler.Rotation.Value = 270);
+
+ ensureValid();
+
+ AddStep("rotate 360", () => tabletHandler.Rotation.Value = 360);
+
+ ensureValid();
+
+ AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0);
+ ensureValid();
+
+ AddStep("rotate 45", () => tabletHandler.Rotation.Value = 45);
+ ensureInvalid();
+
+ AddStep("rotate 0", () => tabletHandler.Rotation.Value = 0);
+ ensureValid();
+ }
+
+ [Test]
+ public void TestOffsetValidity()
+ {
+ ensureValid();
+ AddStep("move right", () => tabletHandler.AreaOffset.Value = Vector2.Zero);
+ ensureInvalid();
+ AddStep("move back", () => tabletHandler.AreaOffset.Value = tabletHandler.AreaSize.Value / 2);
+ ensureValid();
+ }
+
+ private void ensureValid() => AddAssert("area valid", () => settings.AreaSelection.IsWithinBounds);
+
+ private void ensureInvalid() => AddAssert("area invalid", () => !settings.AreaSelection.IsWithinBounds);
+
public class TestTabletHandler : ITabletHandler
{
public Bindable AreaOffset { get; } = new Bindable();
diff --git a/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs b/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs
index 55f43cfe46..1fd03a34e7 100644
--- a/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSampleDebounceComponent.cs
@@ -15,11 +15,6 @@ namespace osu.Game.Graphics.UserInterface
///
public abstract class HoverSampleDebounceComponent : CompositeDrawable
{
- ///
- /// Length of debounce for hover sound playback, in milliseconds.
- ///
- public double HoverDebounceTime { get; } = 20;
-
private Bindable lastPlaybackTime;
[BackgroundDependencyLoader]
@@ -34,7 +29,7 @@ namespace osu.Game.Graphics.UserInterface
if (e.HasAnyButtonPressed)
return false;
- bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime;
+ bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= OsuGameBase.SAMPLE_DEBOUNCE_TIME;
if (enoughTimePassedSinceLastPlayback)
{
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
index c0ef5cb3fc..7db1efc75f 100644
--- a/osu.Game/Graphics/UserInterface/HoverSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -6,7 +6,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
-using osu.Game.Configuration;
using osu.Framework.Utils;
namespace osu.Game.Graphics.UserInterface
@@ -28,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface
}
[BackgroundDependencyLoader]
- private void load(AudioManager audio, SessionStatics statics)
+ private void load(AudioManager audio)
{
sampleHover = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-hover")
?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-hover");
diff --git a/osu.Game/Input/RealmKeyBindingStore.cs b/osu.Game/Input/RealmKeyBindingStore.cs
index 9089169877..03cb4031ca 100644
--- a/osu.Game/Input/RealmKeyBindingStore.cs
+++ b/osu.Game/Input/RealmKeyBindingStore.cs
@@ -46,52 +46,53 @@ namespace osu.Game.Input
}
///
- /// Register a new type of , adding default bindings from .
+ /// Register all defaults for this store.
///
/// The container to populate defaults from.
- public void Register(KeyBindingContainer container) => insertDefaults(container.DefaultKeyBindings);
-
- ///
- /// Register a ruleset, adding default bindings for each of its variants.
- ///
- /// The ruleset to populate defaults from.
- public void Register(RulesetInfo ruleset)
- {
- var instance = ruleset.CreateInstance();
-
- foreach (var variant in instance.AvailableVariants)
- insertDefaults(instance.GetDefaultKeyBindings(variant), ruleset.ID, variant);
- }
-
- private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null)
+ /// The rulesets to populate defaults from.
+ public void Register(KeyBindingContainer container, IEnumerable rulesets)
{
using (var usage = realmFactory.GetForWrite())
{
- // compare counts in database vs defaults
- foreach (var defaultsForAction in defaults.GroupBy(k => k.Action))
+ // intentionally flattened to a list rather than querying against the IQueryable, as nullable fields being queried against aren't indexed.
+ // this is much faster as a result.
+ var existingBindings = usage.Realm.All().ToList();
+
+ insertDefaults(usage, existingBindings, container.DefaultKeyBindings);
+
+ foreach (var ruleset in rulesets)
{
- int existingCount = usage.Realm.All().Count(k => k.RulesetID == rulesetId && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key);
-
- if (defaultsForAction.Count() <= existingCount)
- continue;
-
- foreach (var k in defaultsForAction.Skip(existingCount))
- {
- // insert any defaults which are missing.
- usage.Realm.Add(new RealmKeyBinding
- {
- KeyCombinationString = k.KeyCombination.ToString(),
- ActionInt = (int)k.Action,
- RulesetID = rulesetId,
- Variant = variant
- });
- }
+ var instance = ruleset.CreateInstance();
+ foreach (var variant in instance.AvailableVariants)
+ insertDefaults(usage, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant);
}
usage.Commit();
}
}
+ private void insertDefaults(RealmContextFactory.RealmUsage usage, List existingBindings, IEnumerable defaults, int? rulesetId = null, int? variant = null)
+ {
+ // compare counts in database vs defaults for each action type.
+ foreach (var defaultsForAction in defaults.GroupBy(k => k.Action))
+ {
+ // avoid performing redundant queries when the database is empty and needs to be re-filled.
+ int existingCount = existingBindings.Count(k => k.RulesetID == rulesetId && k.Variant == variant && k.ActionInt == (int)defaultsForAction.Key);
+
+ if (defaultsForAction.Count() <= existingCount)
+ continue;
+
+ // insert any defaults which are missing.
+ usage.Realm.Add(defaultsForAction.Skip(existingCount).Select(k => new RealmKeyBinding
+ {
+ KeyCombinationString = k.KeyCombination.ToString(),
+ ActionInt = (int)k.Action,
+ RulesetID = rulesetId,
+ Variant = variant
+ }));
+ }
+ }
+
///
/// Keys which should not be allowed for gameplay input purposes.
///
diff --git a/osu.Game/Online/API/Requests/GetUserRequest.cs b/osu.Game/Online/API/Requests/GetUserRequest.cs
index 42aad6f9eb..e49c4ab298 100644
--- a/osu.Game/Online/API/Requests/GetUserRequest.cs
+++ b/osu.Game/Online/API/Requests/GetUserRequest.cs
@@ -8,15 +8,47 @@ namespace osu.Game.Online.API.Requests
{
public class GetUserRequest : APIRequest
{
- private readonly long? userId;
+ private readonly string lookup;
public readonly RulesetInfo Ruleset;
+ private readonly LookupType lookupType;
+ ///
+ /// Gets the currently logged-in user.
+ ///
+ public GetUserRequest()
+ {
+ }
+
+ ///
+ /// Gets a user from their ID.
+ ///
+ /// The user to get.
+ /// The ruleset to get the user's info for.
public GetUserRequest(long? userId = null, RulesetInfo ruleset = null)
{
- this.userId = userId;
+ lookup = userId.ToString();
+ lookupType = LookupType.Id;
Ruleset = ruleset;
}
- protected override string Target => userId.HasValue ? $@"users/{userId}/{Ruleset?.ShortName}" : $@"me/{Ruleset?.ShortName}";
+ ///
+ /// Gets a user from their username.
+ ///
+ /// The user to get.
+ /// The ruleset to get the user's info for.
+ public GetUserRequest(string username = null, RulesetInfo ruleset = null)
+ {
+ lookup = username;
+ lookupType = LookupType.Username;
+ Ruleset = ruleset;
+ }
+
+ protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?k={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}";
+
+ private enum LookupType
+ {
+ Id,
+ Username
+ }
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 187669cbb4..2107b3a0e9 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -333,6 +333,9 @@ namespace osu.Game
case LinkAction.OpenUserProfile:
if (int.TryParse(link.Argument, out int userId))
ShowUser(userId);
+ else
+ ShowUser(link.Argument);
+
break;
case LinkAction.OpenWiki:
@@ -380,6 +383,12 @@ namespace osu.Game
/// The user to display.
public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId));
+ ///
+ /// Show a user's profile as an overlay.
+ ///
+ /// The user to display.
+ public void ShowUser(string username) => waitForReady(() => userProfile, _ => userProfile.ShowUser(username));
+
///
/// Show a beatmap's set as an overlay, displaying the given beatmap.
///
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 16e3cdbfda..f4db0f2603 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -56,6 +56,11 @@ namespace osu.Game
public const int SAMPLE_CONCURRENCY = 6;
+ ///
+ /// Length of debounce (in milliseconds) for commonly occuring sample playbacks that could stack.
+ ///
+ public const int SAMPLE_DEBOUNCE_TIME = 20;
+
///
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
///
@@ -200,31 +205,7 @@ namespace osu.Game
dependencies.CacheAs(this);
dependencies.CacheAs(LocalConfig);
- AddFont(Resources, @"Fonts/osuFont");
-
- AddFont(Resources, @"Fonts/Torus/Torus-Regular");
- AddFont(Resources, @"Fonts/Torus/Torus-Light");
- AddFont(Resources, @"Fonts/Torus/Torus-SemiBold");
- AddFont(Resources, @"Fonts/Torus/Torus-Bold");
-
- AddFont(Resources, @"Fonts/Inter/Inter-Regular");
- AddFont(Resources, @"Fonts/Inter/Inter-RegularItalic");
- AddFont(Resources, @"Fonts/Inter/Inter-Light");
- AddFont(Resources, @"Fonts/Inter/Inter-LightItalic");
- AddFont(Resources, @"Fonts/Inter/Inter-SemiBold");
- AddFont(Resources, @"Fonts/Inter/Inter-SemiBoldItalic");
- AddFont(Resources, @"Fonts/Inter/Inter-Bold");
- AddFont(Resources, @"Fonts/Inter/Inter-BoldItalic");
-
- AddFont(Resources, @"Fonts/Noto/Noto-Basic");
- AddFont(Resources, @"Fonts/Noto/Noto-Hangul");
- AddFont(Resources, @"Fonts/Noto/Noto-CJK-Basic");
- AddFont(Resources, @"Fonts/Noto/Noto-CJK-Compatibility");
- AddFont(Resources, @"Fonts/Noto/Noto-Thai");
-
- AddFont(Resources, @"Fonts/Venera/Venera-Light");
- AddFont(Resources, @"Fonts/Venera/Venera-Bold");
- AddFont(Resources, @"Fonts/Venera/Venera-Black");
+ InitialiseFonts();
Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY;
@@ -346,10 +327,7 @@ namespace osu.Game
base.Content.Add(CreateScalingContainer().WithChildren(mainContent));
KeyBindingStore = new RealmKeyBindingStore(realmFactory);
- KeyBindingStore.Register(globalBindings);
-
- foreach (var r in RulesetStore.AvailableRulesets)
- KeyBindingStore.Register(r);
+ KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets);
dependencies.Cache(globalBindings);
@@ -363,6 +341,35 @@ namespace osu.Game
Ruleset.BindValueChanged(onRulesetChanged);
}
+ protected virtual void InitialiseFonts()
+ {
+ AddFont(Resources, @"Fonts/osuFont");
+
+ AddFont(Resources, @"Fonts/Torus/Torus-Regular");
+ AddFont(Resources, @"Fonts/Torus/Torus-Light");
+ AddFont(Resources, @"Fonts/Torus/Torus-SemiBold");
+ AddFont(Resources, @"Fonts/Torus/Torus-Bold");
+
+ AddFont(Resources, @"Fonts/Inter/Inter-Regular");
+ AddFont(Resources, @"Fonts/Inter/Inter-RegularItalic");
+ AddFont(Resources, @"Fonts/Inter/Inter-Light");
+ AddFont(Resources, @"Fonts/Inter/Inter-LightItalic");
+ AddFont(Resources, @"Fonts/Inter/Inter-SemiBold");
+ AddFont(Resources, @"Fonts/Inter/Inter-SemiBoldItalic");
+ AddFont(Resources, @"Fonts/Inter/Inter-Bold");
+ AddFont(Resources, @"Fonts/Inter/Inter-BoldItalic");
+
+ AddFont(Resources, @"Fonts/Noto/Noto-Basic");
+ AddFont(Resources, @"Fonts/Noto/Noto-Hangul");
+ AddFont(Resources, @"Fonts/Noto/Noto-CJK-Basic");
+ AddFont(Resources, @"Fonts/Noto/Noto-CJK-Compatibility");
+ AddFont(Resources, @"Fonts/Noto/Noto-Thai");
+
+ AddFont(Resources, @"Fonts/Venera/Venera-Light");
+ AddFont(Resources, @"Fonts/Venera/Venera-Bold");
+ AddFont(Resources, @"Fonts/Venera/Venera-Black");
+ }
+
private IDisposable blocking;
private void updateThreadStateChanged(ValueChangedEvent state)
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 2175e17da9..8809dec642 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -9,6 +9,7 @@ using osu.Game.Overlays.Notifications;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Framework.Threading;
@@ -29,6 +30,9 @@ namespace osu.Game.Overlays
private FlowContainer sections;
+ [Resolved]
+ private AudioManager audio { get; set; }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -98,14 +102,18 @@ namespace osu.Game.Overlays
private int runningDepth;
- private void notificationClosed() => updateCounts();
-
private readonly Scheduler postScheduler = new Scheduler();
public override bool IsPresent => base.IsPresent || postScheduler.HasPendingTasks;
private bool processingPosts = true;
+ private double? lastSamplePlayback;
+
+ ///
+ /// Post a new notification for display.
+ ///
+ /// The notification to display.
public void Post(Notification notification) => postScheduler.Add(() =>
{
++runningDepth;
@@ -124,11 +132,13 @@ namespace osu.Game.Overlays
Show();
updateCounts();
+ playDebouncedSample(notification.PopInSampleName);
});
protected override void Update()
{
base.Update();
+
if (processingPosts)
postScheduler.Update();
}
@@ -151,6 +161,24 @@ namespace osu.Game.Overlays
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
}
+ private void notificationClosed()
+ {
+ updateCounts();
+
+ // this debounce is currently shared between popin/popout sounds, which means one could potentially not play when the user is expecting it.
+ // popout is constant across all notification types, and should therefore be handled using playback concurrency instead, but seems broken at the moment.
+ playDebouncedSample("UI/overlay-pop-out");
+ }
+
+ private void playDebouncedSample(string sampleName)
+ {
+ if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
+ {
+ audio.Samples.Get(sampleName)?.Play();
+ lastSamplePlayback = Time.Current;
+ }
+ }
+
private void updateCounts()
{
UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum();
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index d1a97c74b2..44203e8ee7 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -3,20 +3,18 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Audio;
-using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
-using osu.Game.Graphics;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
+using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Overlays.Notifications
{
@@ -42,10 +40,7 @@ namespace osu.Game.Overlays.Notifications
///
public virtual bool DisplayOnTop => true;
- private Sample samplePopIn;
- private Sample samplePopOut;
- protected virtual string PopInSampleName => "UI/notification-pop-in";
- protected virtual string PopOutSampleName => "UI/overlay-pop-out"; // TODO: replace with a unique sample?
+ public virtual string PopInSampleName => "UI/notification-pop-in";
protected NotificationLight Light;
private readonly CloseButton closeButton;
@@ -114,7 +109,7 @@ namespace osu.Game.Overlays.Notifications
closeButton = new CloseButton
{
Alpha = 0,
- Action = () => Close(),
+ Action = Close,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding
@@ -127,13 +122,6 @@ namespace osu.Game.Overlays.Notifications
});
}
- [BackgroundDependencyLoader]
- private void load(AudioManager audio)
- {
- samplePopIn = audio.Samples.Get(PopInSampleName);
- samplePopOut = audio.Samples.Get(PopOutSampleName);
- }
-
protected override bool OnHover(HoverEvent e)
{
closeButton.FadeIn(75);
@@ -158,8 +146,6 @@ namespace osu.Game.Overlays.Notifications
{
base.LoadComplete();
- samplePopIn?.Play();
-
this.FadeInFromZero(200);
NotificationContent.MoveToX(DrawSize.X);
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
@@ -167,15 +153,12 @@ namespace osu.Game.Overlays.Notifications
public bool WasClosed;
- public virtual void Close(bool playSound = true)
+ public virtual void Close()
{
if (WasClosed) return;
WasClosed = true;
- if (playSound)
- samplePopOut?.Play();
-
Closed?.Invoke();
this.FadeOut(100);
Expire();
diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs
index 2316199049..a23ff07a64 100644
--- a/osu.Game/Overlays/Notifications/NotificationSection.cs
+++ b/osu.Game/Overlays/Notifications/NotificationSection.cs
@@ -110,12 +110,7 @@ namespace osu.Game.Overlays.Notifications
private void clearAll()
{
- bool first = true;
- notifications.Children.ForEach(c =>
- {
- c.Close(first);
- first = false;
- });
+ notifications.Children.ForEach(c => c.Close());
}
protected override void Update()
diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs
index 703c14af2b..3105ecd742 100644
--- a/osu.Game/Overlays/Notifications/ProgressNotification.cs
+++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs
@@ -150,12 +150,12 @@ namespace osu.Game.Overlays.Notifications
colourCancelled = colours.Red;
}
- public override void Close(bool playSound = true)
+ public override void Close()
{
switch (State)
{
case ProgressNotificationState.Cancelled:
- base.Close(playSound);
+ base.Close();
break;
case ProgressNotificationState.Active:
diff --git a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
index 13c9c5a02d..faab4ed472 100644
--- a/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
+++ b/osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
@@ -7,7 +7,7 @@ namespace osu.Game.Overlays.Notifications
{
public class SimpleErrorNotification : SimpleNotification
{
- protected override string PopInSampleName => "UI/error-notification-pop-in";
+ public override string PopInSampleName => "UI/error-notification-pop-in";
public SimpleErrorNotification()
{
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
index 412889d210..58abfab29c 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletAreaSelection.cs
@@ -4,10 +4,13 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.MatrixExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Handlers.Tablet;
+using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
@@ -17,6 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
public class TabletAreaSelection : CompositeDrawable
{
+ public bool IsWithinBounds { get; private set; }
+
private readonly ITabletHandler handler;
private Container tabletContainer;
@@ -109,29 +114,30 @@ namespace osu.Game.Overlays.Settings.Sections.Input
areaOffset.BindTo(handler.AreaOffset);
areaOffset.BindValueChanged(val =>
{
- usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint)
- .OnComplete(_ => checkBounds()); // required as we are using SSDQ.
+ usableAreaContainer.MoveTo(val.NewValue, 100, Easing.OutQuint);
+ checkBounds();
}, true);
areaSize.BindTo(handler.AreaSize);
areaSize.BindValueChanged(val =>
{
- usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint)
- .OnComplete(_ => checkBounds()); // required as we are using SSDQ.
+ usableAreaContainer.ResizeTo(val.NewValue, 100, Easing.OutQuint);
int x = (int)val.NewValue.X;
int y = (int)val.NewValue.Y;
int commonDivider = greatestCommonDivider(x, y);
usableAreaText.Text = $"{(float)x / commonDivider}:{(float)y / commonDivider}";
+ checkBounds();
}, true);
rotation.BindTo(handler.Rotation);
rotation.BindValueChanged(val =>
{
+ usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint);
tabletContainer.RotateTo(-val.NewValue, 800, Easing.OutQuint);
- usableAreaContainer.RotateTo(val.NewValue, 100, Easing.OutQuint)
- .OnComplete(_ => checkBounds()); // required as we are using SSDQ.
+
+ checkBounds();
}, true);
tablet.BindTo(handler.Tablet);
@@ -169,12 +175,35 @@ namespace osu.Game.Overlays.Settings.Sections.Input
if (tablet.Value == null)
return;
- var usableSsdq = usableAreaContainer.ScreenSpaceDrawQuad;
+ // allow for some degree of floating point error, as we don't care about being perfect here.
+ const float lenience = 0.5f;
- bool isWithinBounds = tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.TopLeft + new Vector2(1)) &&
- tabletContainer.ScreenSpaceDrawQuad.Contains(usableSsdq.BottomRight - new Vector2(1));
+ var tabletArea = new Quad(-lenience, -lenience, tablet.Value.Size.X + lenience * 2, tablet.Value.Size.Y + lenience * 2);
- usableFill.FadeColour(isWithinBounds ? colour.Blue : colour.RedLight, 100);
+ var halfUsableArea = areaSize.Value / 2;
+ var offset = areaOffset.Value;
+
+ var usableAreaQuad = new Quad(
+ new Vector2(-halfUsableArea.X, -halfUsableArea.Y),
+ new Vector2(halfUsableArea.X, -halfUsableArea.Y),
+ new Vector2(-halfUsableArea.X, halfUsableArea.Y),
+ new Vector2(halfUsableArea.X, halfUsableArea.Y)
+ );
+
+ var matrix = Matrix3.Identity;
+
+ MatrixExtensions.TranslateFromLeft(ref matrix, offset);
+ MatrixExtensions.RotateFromLeft(ref matrix, MathUtils.DegreesToRadians(rotation.Value));
+
+ usableAreaQuad *= matrix;
+
+ IsWithinBounds =
+ tabletArea.Contains(usableAreaQuad.TopLeft) &&
+ tabletArea.Contains(usableAreaQuad.TopRight) &&
+ tabletArea.Contains(usableAreaQuad.BottomLeft) &&
+ tabletArea.Contains(usableAreaQuad.BottomRight);
+
+ usableFill.FadeColour(IsWithinBounds ? colour.Blue : colour.RedLight, 100);
}
protected override void Update()
diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
index b8b86d9069..8c60e81fb5 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
public class TabletSettings : SettingsSubsection
{
+ public TabletAreaSelection AreaSelection { get; private set; }
+
private readonly ITabletHandler tabletHandler;
private readonly Bindable enabled = new BindableBool(true);
@@ -121,7 +123,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new TabletAreaSelection(tabletHandler)
+ AreaSelection = new TabletAreaSelection(tabletHandler)
{
RelativeSizeAxes = Axes.X,
Height = 300,
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index 299a14b250..b0327987f2 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -40,6 +40,8 @@ namespace osu.Game.Overlays
public void ShowUser(int userId) => ShowUser(new User { Id = userId });
+ public void ShowUser(string username) => ShowUser(new User { Username = username });
+
public void ShowUser(User user, bool fetchOnline = true)
{
if (user == User.SYSTEM_USER)
@@ -116,7 +118,7 @@ namespace osu.Game.Overlays
if (fetchOnline)
{
- userReq = new GetUserRequest(user.Id);
+ userReq = user.Id > 1 ? new GetUserRequest(user.Id) : new GetUserRequest(user.Username);
userReq.Success += userLoadComplete;
API.Queue(userReq);
}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index 80be61ead1..de62cf8d33 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -28,6 +28,7 @@ using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Testing;
using osu.Game.Extensions;
using osu.Game.Rulesets.Filter;
+using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets
@@ -315,5 +316,11 @@ namespace osu.Game.Rulesets
///
[CanBeNull]
public virtual IRulesetFilterCriteria CreateRulesetFilterCriteria() => null;
+
+ ///
+ /// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen.
+ ///
+ [CanBeNull]
+ public virtual RulesetSetupSection CreateEditorSetupSection() => null;
}
}
diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs
index 294f23d2ac..9f562a618e 100644
--- a/osu.Game/Screens/BackgroundScreenStack.cs
+++ b/osu.Game/Screens/BackgroundScreenStack.cs
@@ -17,15 +17,21 @@ namespace osu.Game.Screens
Origin = Anchor.Centre;
}
- public void Push(BackgroundScreen screen)
+ ///
+ /// Attempt to push a new background screen to this stack.
+ ///
+ /// The screen to attempt to push.
+ /// Whether the push succeeded. For example, if the existing screen was already of the correct type this will return false.
+ public bool Push(BackgroundScreen screen)
{
if (screen == null)
- return;
+ return false;
if (EqualityComparer.Default.Equals((BackgroundScreen)CurrentScreen, screen))
- return;
+ return false;
base.Push(screen);
+ return true;
}
}
}
diff --git a/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs
new file mode 100644
index 0000000000..935842ff99
--- /dev/null
+++ b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs
@@ -0,0 +1,20 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Localisation;
+using osu.Game.Rulesets;
+
+namespace osu.Game.Screens.Edit.Setup
+{
+ public abstract class RulesetSetupSection : SetupSection
+ {
+ public sealed override LocalisableString Title => $"Ruleset ({rulesetInfo.Name})";
+
+ private readonly RulesetInfo rulesetInfo;
+
+ protected RulesetSetupSection(RulesetInfo rulesetInfo)
+ {
+ this.rulesetInfo = rulesetInfo;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs
index 746cf38867..04767f1786 100644
--- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs
+++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
@@ -10,7 +11,7 @@ namespace osu.Game.Screens.Edit.Setup
public class SetupScreen : EditorRoundedScreen
{
[Cached]
- private SectionsContainer sections = new SectionsContainer();
+ private SectionsContainer sections { get; } = new SetupScreenSectionsContainer();
[Cached]
private SetupScreenHeader header = new SetupScreenHeader();
@@ -21,24 +22,27 @@ namespace osu.Game.Screens.Edit.Setup
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(EditorBeatmap beatmap)
{
- AddRange(new Drawable[]
+ var sectionsEnumerable = new List
{
- sections = new SetupScreenSectionsContainer
- {
- FixedHeader = header,
- RelativeSizeAxes = Axes.Both,
- Children = new SetupSection[]
- {
- new ResourcesSection(),
- new MetadataSection(),
- new DifficultySection(),
- new ColoursSection(),
- new DesignSection(),
- }
- },
- });
+ new ResourcesSection(),
+ new MetadataSection(),
+ new DifficultySection(),
+ new ColoursSection(),
+ new DesignSection(),
+ };
+
+ var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateEditorSetupSection();
+ if (rulesetSpecificSection != null)
+ sectionsEnumerable.Add(rulesetSpecificSection);
+
+ Add(sections.With(s =>
+ {
+ s.RelativeSizeAxes = Axes.Both;
+ s.ChildrenEnumerable = sectionsEnumerable;
+ s.FixedHeader = header;
+ }));
}
private class SetupScreenSectionsContainer : SectionsContainer
diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs
index 1f988d62e2..1dde6fb926 100644
--- a/osu.Game/Screens/Edit/Setup/SetupSection.cs
+++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs
@@ -12,9 +12,9 @@ using osuTK;
namespace osu.Game.Screens.Edit.Setup
{
- internal abstract class SetupSection : Container
+ public abstract class SetupSection : Container
{
- private readonly FillFlowContainer flow;
+ private FillFlowContainer flow;
///
/// Used to align some of the child s together to achieve a grid-like look.
@@ -31,7 +31,8 @@ namespace osu.Game.Screens.Edit.Setup
public abstract LocalisableString Title { get; }
- protected SetupSection()
+ [BackgroundDependencyLoader]
+ private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index e3fe14a585..9aec2a5c19 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -186,17 +186,14 @@ namespace osu.Game.Screens
{
applyArrivingDefaults(false);
- backgroundStack?.Push(ownedBackground = CreateBackground());
-
- background = backgroundStack?.CurrentScreen as BackgroundScreen;
-
- if (background != ownedBackground)
+ if (backgroundStack?.Push(ownedBackground = CreateBackground()) != true)
{
- // background may have not been replaced, at which point we don't want to track the background lifetime.
+ // If the constructed instance was not actually pushed to the background stack, we don't want to track it unnecessarily.
ownedBackground?.Dispose();
ownedBackground = null;
}
+ background = backgroundStack?.CurrentScreen as BackgroundScreen;
base.OnEntering(last);
}
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index ef9181c8a6..03434961ea 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -367,6 +367,11 @@ namespace osu.Game.Tests.Visual
Add(runner = new TestSceneTestRunner.TestRunner());
}
+ protected override void InitialiseFonts()
+ {
+ // skip fonts load as it's not required for testing purposes.
+ }
+
public void RunTestBlocking(TestScene test) => runner.RunTestBlocking(test);
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index ebe3de6ea4..ae423bac8c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -37,7 +37,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 1714bae53c..be737392e1 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -71,7 +71,7 @@
-
+