From 0fe121f48a8954d25fef2b8059fea1f60d967549 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:25:22 +0300 Subject: [PATCH 01/76] Move and rename settings toolbox group test scene --- .../TestSceneSettingsToolboxGroup.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename osu.Game.Tests/Visual/{Gameplay/TestSceneReplaySettingsOverlay.cs => UserInterface/TestSceneSettingsToolboxGroup.cs} (85%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs similarity index 85% rename from osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index f8fab784cc..5ca30d07b0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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; @@ -8,12 +8,12 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; -namespace osu.Game.Tests.Visual.Gameplay +namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneReplaySettingsOverlay : OsuTestScene + public class TestSceneSettingsToolboxGroup : OsuTestScene { - public TestSceneReplaySettingsOverlay() + public TestSceneSettingsToolboxGroup() { ExampleContainer container; From deda1c83e67779a748d82138215846519dbcfea7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:44:18 +0300 Subject: [PATCH 02/76] Add failing test case --- .../TestSceneSettingsToolboxGroup.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index 5ca30d07b0..76657afbcd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -1,17 +1,21 @@ // 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.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneSettingsToolboxGroup : OsuTestScene + public class TestSceneSettingsToolboxGroup : OsuManualInputManagerTestScene { public TestSceneSettingsToolboxGroup() { @@ -46,6 +50,22 @@ namespace osu.Game.Tests.Visual.UserInterface })); } + [Test] + public void TestClickExpandButtonMultipleTimes() + { + SettingsToolboxGroup group = null; + + AddAssert("group expanded by default", () => (group = this.ChildrenOfType().First()).Expanded.Value); + AddStep("click expand button multiple times", () => + { + InputManager.MoveMouseTo(group.ChildrenOfType().Single()); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 100); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 200); + Scheduler.AddDelayed(() => InputManager.Click(MouseButton.Left), 300); + }); + AddAssert("group contracted", () => !group.Expanded.Value); + } + private class ExampleContainer : PlayerSettingsGroup { public ExampleContainer() From 702c6ae658d671a21985f28ec199b3bc18d7f5bd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 13:49:31 +0300 Subject: [PATCH 03/76] Fix `SettingsToolboxGroup` not clearing transforms before updating autosize --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 808d4fc422..36d4f03e02 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -156,6 +156,8 @@ namespace osu.Game.Overlays private void updateExpandedState(ValueChangedEvent expanded) { + content.ClearTransforms(); + if (expanded.NewValue) content.AutoSizeAxes = Axes.Y; else From ceb6276d2ffd3e0e9b900225ea28bd12efdca146 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:19:29 +0300 Subject: [PATCH 04/76] Add failing test case --- .../UserInterface/TestSceneRoundedButton.cs | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs index 9ccfba7c74..ef3c0c7fa4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -2,11 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osuTK; namespace osu.Game.Tests.Visual.UserInterface { @@ -22,15 +27,10 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.DarkGray - }, button = new RoundedButton { Width = 400, - Text = "Test button", + Text = "Test Button", Anchor = Anchor.Centre, Origin = Anchor.Centre, Action = () => { } @@ -40,5 +40,39 @@ namespace osu.Game.Tests.Visual.UserInterface AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { }); } + + [Test] + public void TestOverlay() + { + IEnumerable schemes = Enum.GetValues(typeof(OverlayColourScheme)).Cast(); + + AddStep("create buttons", () => + { + Child = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + ChildrenEnumerable = schemes.Select(c => new DependencyProvidingContainer + { + AutoSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] { (typeof(OverlayColourProvider), new OverlayColourProvider(c)) }, + Child = new RoundedButton + { + Width = 400, + Text = $"Test {c}", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Action = () => { }, + } + }), + }; + }); + + AddAssert("first button has correct colour", () => this.ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(schemes.First()).Highlight1); + AddToggleStep("toggle disabled", disabled => this.ChildrenOfType().ForEach(b => b.Action = disabled ? (Action)null : () => { })); + } } } From 172524ff8a3812ffce89361c5552a6426529df33 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:21:00 +0300 Subject: [PATCH 05/76] Move default background colour specification to `OsuButton` --- osu.Game/Graphics/UserInterface/OsuButton.cs | 8 +++++--- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 11 ----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 29a797bd78..6fa63e5874 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,6 +13,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -86,11 +88,11 @@ namespace osu.Game.Graphics.UserInterface AddInternal(new HoverClickSounds(hoverSounds.Value)); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + [BackgroundDependencyLoader(permitNulls: true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { if (backgroundColour == null) - BackgroundColour = colours.BlueDark; + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index f535a32b39..b1529774d9 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -2,13 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using JetBrains.Annotations; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -26,13 +22,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } - [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) - { - if (BackgroundColour == Color4.White) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; - } - protected override void LoadComplete() { base.LoadComplete(); From ace25af949ab39946e4a8b57103b40247c96dbfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 20:55:26 +0300 Subject: [PATCH 06/76] Revert "Move default background colour specification to `OsuButton`" This reverts commit 172524ff8a3812ffce89361c5552a6426529df33. --- osu.Game/Graphics/UserInterface/OsuButton.cs | 8 +++----- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 11 +++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 6fa63e5874..29a797bd78 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -13,7 +12,6 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface @@ -88,11 +86,11 @@ namespace osu.Game.Graphics.UserInterface AddInternal(new HoverClickSounds(hoverSounds.Value)); } - [BackgroundDependencyLoader(permitNulls: true)] - private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { if (backgroundColour == null) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + BackgroundColour = colours.BlueDark; } protected override void LoadComplete() diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index b1529774d9..f535a32b39 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -2,9 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -22,6 +26,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) + { + if (BackgroundColour == Color4.White) + BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + } + protected override void LoadComplete() { base.LoadComplete(); From 1fcfeac05f8c499ae86c36d32bcfdaf0bfc736f4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 18:52:09 +0300 Subject: [PATCH 07/76] Fix `RoundedButton` not using its default background colour --- osu.Game/Graphics/UserInterface/OsuButton.cs | 31 ++++++++++++++----- .../Graphics/UserInterfaceV2/RoundedButton.cs | 4 +-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 29a797bd78..08514d94c3 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -33,9 +32,12 @@ namespace osu.Game.Graphics.UserInterface private Color4? backgroundColour; + /// + /// Sets a custom background colour to this button, replacing the provided default. + /// public Color4 BackgroundColour { - get => backgroundColour ?? Color4.White; + get => backgroundColour ?? defaultBackgroundColour; set { backgroundColour = value; @@ -43,6 +45,23 @@ namespace osu.Game.Graphics.UserInterface } } + private Color4 defaultBackgroundColour; + + /// + /// Sets a default background colour to this button. + /// + protected Color4 DefaultBackgroundColour + { + get => defaultBackgroundColour; + set + { + defaultBackgroundColour = value; + + if (backgroundColour == null) + Background.FadeColour(value); + } + } + protected override Container Content { get; } protected Box Hover; @@ -89,8 +108,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (backgroundColour == null) - BackgroundColour = colours.BlueDark; + DefaultBackgroundColour = colours.BlueDark; } protected override void LoadComplete() @@ -106,10 +124,7 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnClick(ClickEvent e) { if (Enabled.Value) - { - Debug.Assert(backgroundColour != null); - Background.FlashColour(backgroundColour.Value.Lighten(0.4f), 200); - } + Background.FlashColour(BackgroundColour.Lighten(0.4f), 200); return base.OnClick(e); } diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index f535a32b39..ec56b6d784 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; -using osuTK.Graphics; namespace osu.Game.Graphics.UserInterfaceV2 { @@ -29,8 +28,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 [BackgroundDependencyLoader(true)] private void load([CanBeNull] OverlayColourProvider overlayColourProvider, OsuColour colours) { - if (BackgroundColour == Color4.White) - BackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; + DefaultBackgroundColour = overlayColourProvider?.Highlight1 ?? colours.Blue3; } protected override void LoadComplete() From 5726cf660f7ec219804fb0ebb6315ab5af5017ed Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 21:19:34 +0300 Subject: [PATCH 08/76] Improve test coverage to use existing `ThemeComparisonTestScene` --- .../UserInterface/TestSceneRoundedButton.cs | 74 +++++-------------- 1 file changed, 18 insertions(+), 56 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs index ef3c0c7fa4..f45c55d912 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneRoundedButton.cs @@ -1,78 +1,40 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osuTK; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneRoundedButton : OsuTestScene + public class TestSceneRoundedButton : ThemeComparisonTestScene { - [Test] - public void TestBasic() + private readonly BindableBool enabled = new BindableBool(true); + + protected override Drawable CreateContent() => new RoundedButton { - RoundedButton button = null; + Width = 400, + Text = "Test button", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Enabled = { BindTarget = enabled }, + }; - AddStep("create button", () => Child = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - button = new RoundedButton - { - Width = 400, - Text = "Test Button", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => { } - } - } - }); - - AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { }); + [Test] + public void TestDisabled() + { + AddToggleStep("toggle disabled", disabled => enabled.Value = !disabled); } [Test] - public void TestOverlay() + public void TestBackgroundColour() { - IEnumerable schemes = Enum.GetValues(typeof(OverlayColourScheme)).Cast(); - - AddStep("create buttons", () => - { - Child = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(5f), - ChildrenEnumerable = schemes.Select(c => new DependencyProvidingContainer - { - AutoSizeAxes = Axes.Both, - CachedDependencies = new (Type, object)[] { (typeof(OverlayColourProvider), new OverlayColourProvider(c)) }, - Child = new RoundedButton - { - Width = 400, - Text = $"Test {c}", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Action = () => { }, - } - }), - }; - }); - - AddAssert("first button has correct colour", () => this.ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(schemes.First()).Highlight1); - AddToggleStep("toggle disabled", disabled => this.ChildrenOfType().ForEach(b => b.Action = disabled ? (Action)null : () => { })); + AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red)); + AddAssert("first button has correct colour", () => Cell(0, 1).ChildrenOfType().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Highlight1); } } } From 96ea4ee7b33136a4fb563520c730c971eaf63026 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 23:49:05 +0300 Subject: [PATCH 09/76] Add explanatory comment --- osu.Game/Overlays/SettingsToolboxGroup.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index 36d4f03e02..077762c0d0 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -156,6 +156,8 @@ namespace osu.Game.Overlays private void updateExpandedState(ValueChangedEvent expanded) { + // clearing transforms is necessary to avoid a previous height transform + // potentially continuing to get processed while content has changed to autosize. content.ClearTransforms(); if (expanded.NewValue) From beb86a7f7c909f27ba0f8fa217338e32a067bac4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 9 May 2022 23:57:08 +0300 Subject: [PATCH 10/76] Remove usage of player settings in `TestSceneSettingsToolboxGroup` --- .../TestSceneSettingsToolboxGroup.cs | 73 ++++++++----------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs index 76657afbcd..8ef24e58a0 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSettingsToolboxGroup.cs @@ -4,12 +4,11 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Overlays.Settings; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -17,45 +16,39 @@ namespace osu.Game.Tests.Visual.UserInterface [TestFixture] public class TestSceneSettingsToolboxGroup : OsuManualInputManagerTestScene { - public TestSceneSettingsToolboxGroup() + private SettingsToolboxGroup group; + + [SetUp] + public void SetUp() => Schedule(() => { - ExampleContainer container; - - Add(new PlayerSettingsOverlay + Child = group = new SettingsToolboxGroup("example") { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - State = { Value = Visibility.Visible } - }); - - Add(container = new ExampleContainer()); - - AddStep(@"Add button", () => container.Add(new TriangleButton - { - RelativeSizeAxes = Axes.X, - Text = @"Button", - })); - - AddStep(@"Add checkbox", () => container.Add(new PlayerCheckbox - { - LabelText = "Checkbox", - })); - - AddStep(@"Add textbox", () => container.Add(new FocusedTextBox - { - RelativeSizeAxes = Axes.X, - Height = 30, - PlaceholderText = "Textbox", - HoldFocus = false, - })); - } + Children = new Drawable[] + { + new RoundedButton + { + RelativeSizeAxes = Axes.X, + Text = @"Button", + Enabled = { Value = true }, + }, + new OsuCheckbox + { + LabelText = @"Checkbox", + }, + new OutlinedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = @"Textbox", + } + }, + }; + }); [Test] public void TestClickExpandButtonMultipleTimes() { - SettingsToolboxGroup group = null; - - AddAssert("group expanded by default", () => (group = this.ChildrenOfType().First()).Expanded.Value); + AddAssert("group expanded by default", () => group.Expanded.Value); AddStep("click expand button multiple times", () => { InputManager.MoveMouseTo(group.ChildrenOfType().Single()); @@ -65,13 +58,5 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddAssert("group contracted", () => !group.Expanded.Value); } - - private class ExampleContainer : PlayerSettingsGroup - { - public ExampleContainer() - : base("example") - { - } - } } } From 2b7eeadac0fbf52419050fbd8e4fe69ff93de893 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 14:48:41 +0900 Subject: [PATCH 11/76] Workaround bad performance when selecting all freemods --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 16975cfd1d..1c629b7a4f 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -192,7 +192,8 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); - ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); + if (customisationButton != null) + ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); SelectedMods.BindValueChanged(val => { From 8ccf2ee0759e58e14d8544c32297e410a8c5fb5e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 15:07:08 +0900 Subject: [PATCH 12/76] Add inline comment --- osu.Game/Overlays/Mods/ModSelectScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectScreen.cs index 1c629b7a4f..912c09f05c 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectScreen.cs @@ -192,6 +192,8 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); + // This is an optimisation to prevent refreshing the available settings controls when it can be + // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectScreen). if (customisationButton != null) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); From bcd91ac743885c1bce1df4bf6a84de925004b73d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 10:02:32 +0300 Subject: [PATCH 13/76] Move exception soft-handling logic to `OsuGameBase` --- osu.Desktop/Program.cs | 22 ---------------------- osu.Game/OsuGameBase.cs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index eb9045d9ce..405f0a8006 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -4,8 +4,6 @@ using System; using System.IO; using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; using osu.Desktop.LegacyIpc; using osu.Framework; using osu.Framework.Development; @@ -63,8 +61,6 @@ namespace osu.Desktop using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = true })) { - host.ExceptionThrown += handleException; - if (!host.IsPrimaryInstance) { if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args @@ -131,23 +127,5 @@ namespace osu.Desktop // tools.SetProcessAppUserModelId(); }); } - - private static int allowableExceptions = DebugUtils.IsDebugBuild ? 0 : 1; - - /// - /// Allow a maximum of one unhandled exception, per second of execution. - /// - /// - private static bool handleException(Exception arg) - { - bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; - - Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); - - // restore the stock of allowable exceptions after a short delay. - Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); - - return continueExecution; - } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 324fcada89..c5b69a3637 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; @@ -180,9 +181,16 @@ namespace osu.Game /// protected DatabaseContextFactory EFContextFactory { get; private set; } + /// + /// Number of exceptions to allow before aborting execution. + /// + protected virtual int SoftHandledExceptions => 0; + public OsuGameBase() { Name = @"osu!"; + + allowableExceptions = SoftHandledExceptions; } [BackgroundDependencyLoader] @@ -408,6 +416,8 @@ namespace osu.Game LocalConfig ??= UseDevelopmentServer ? new DevelopmentOsuConfigManager(Storage) : new OsuConfigManager(Storage); + + host.ExceptionThrown += onExceptionThrown; } /// @@ -505,6 +515,23 @@ namespace osu.Game AvailableMods.Value = dict; } + private int allowableExceptions; + + /// + /// Allows a maximum of one unhandled exception, per second of execution. + /// + private bool onExceptionThrown(Exception _) + { + bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; + + Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {SoftHandledExceptions} more allowable exceptions" : "denied")} ."); + + // restore the stock of allowable exceptions after a short delay. + Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); + + return continueExecution; + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -514,6 +541,9 @@ namespace osu.Game LocalConfig?.Dispose(); realm?.Dispose(); + + if (Host != null) + Host.ExceptionThrown -= onExceptionThrown; } } } From 725f5f4dcb00616e5f7f65ab6595918ad2812a8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 10:02:41 +0300 Subject: [PATCH 14/76] Allow one more exception per second in `OsuGame` and `TournamentGame` --- osu.Game.Tournament/TournamentGame.cs | 3 +++ osu.Game/OsuGame.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 7967f54b49..d9db0e6d20 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Development; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -39,6 +40,8 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; + protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 54c4231b06..6961dce910 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,6 +13,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -175,6 +176,8 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); + protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + public OsuGame(string[] args = null) { this.args = args; From dc3c73f72300af57ad18b9671178c0bed2e1206d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:03:03 +0900 Subject: [PATCH 15/76] Enable sentry session tracking --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index d9c8199f75..7e0449cec8 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -25,6 +25,8 @@ namespace osu.Game.Utils var options = new SentryOptions { Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2", + AutoSessionTracking = true, + IsEnvironmentUser = false, Release = game.Version }; From 09c21cde8ca15a02ddd29dba669cb200fa9919fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:08:42 +0900 Subject: [PATCH 16/76] Add log level translation --- osu.Game/Utils/SentryLogger.cs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 7e0449cec8..c39d718d62 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -50,12 +50,37 @@ namespace osu.Game.Utils if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; lastException = exception; - sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message }, sentryScope); + sentry.CaptureEvent(new SentryEvent(exception) + { + Message = entry.Message, + Level = getSentryLevel(entry.Level), + }, sentryScope); } else sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation"); } + private SentryLevel? getSentryLevel(LogLevel entryLevel) + { + switch (entryLevel) + { + case LogLevel.Debug: + return SentryLevel.Debug; + + case LogLevel.Verbose: + return SentryLevel.Info; + + case LogLevel.Important: + return SentryLevel.Warning; + + case LogLevel.Error: + return SentryLevel.Error; + + default: + throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null); + } + } + private bool shouldSubmitException(Exception exception) { switch (exception) From 64cc6ebddbb090edc7a39bb83415b68dc7f25ece Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:12:31 +0900 Subject: [PATCH 17/76] Add local user tracking to sentry reporting --- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Utils/SentryLogger.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 324fcada89..3f0610e9aa 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -125,7 +125,7 @@ namespace osu.Game protected MusicController MusicController { get; private set; } - protected IAPIProvider API { get; set; } + protected internal IAPIProvider API { get; protected set; } protected Storage Storage { get; set; } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index c39d718d62..5cecc4d776 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -4,7 +4,10 @@ using System; using System.IO; using System.Net; +using JetBrains.Annotations; +using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Online.API.Requests.Responses; using Sentry; namespace osu.Game.Utils @@ -18,6 +21,9 @@ namespace osu.Game.Utils private Scope sentryScope; private Exception lastException; + [UsedImplicitly] + private readonly IBindable localUser; + public SentryLogger(OsuGame game) { if (!game.IsDeployedBuild) return; @@ -34,6 +40,16 @@ namespace osu.Game.Utils sentryScope = new Scope(options); Logger.NewEntry += processLogEntry; + + localUser = game.API.LocalUser.GetBoundCopy(); + localUser.BindValueChanged(user => + { + sentryScope.User = new User + { + Username = user.NewValue.Username, + Id = user.NewValue.Id.ToString(), + }; + }); } private void processLogEntry(LogEntry entry) @@ -50,6 +66,7 @@ namespace osu.Game.Utils if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; lastException = exception; + sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, From a5b454edc72537daaaf428bca8b34bf8a9c926b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:21:49 +0900 Subject: [PATCH 18/76] Remove unnecessary DI caching of `SentryLogger` --- osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs | 2 -- osu.Game/OsuGame.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 0f8337deb6..e4871f611e 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -23,7 +23,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Menu; using osu.Game.Skinning; -using osu.Game.Utils; namespace osu.Game.Tests.Visual.Navigation { @@ -33,7 +32,6 @@ namespace osu.Game.Tests.Visual.Navigation private IReadOnlyList requiredGameDependencies => new[] { typeof(OsuGame), - typeof(SentryLogger), typeof(OsuLogo), typeof(IdleTracker), typeof(OnScreenDisplay), diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 54c4231b06..a6c57998b0 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -258,8 +258,6 @@ namespace osu.Game { dependencies.CacheAs(this); - dependencies.Cache(SentryLogger); - dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); // bind config int to database RulesetInfo From 3338bffce3809d78d0f00e6b0b2bf332e62c8af7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:25:10 +0900 Subject: [PATCH 19/76] Attach user to sentry later in startup flow --- osu.Game/OsuGame.cs | 2 ++ osu.Game/OsuGameBase.cs | 2 +- osu.Game/Utils/SentryLogger.cs | 26 +++++++++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a6c57998b0..b8abef38a8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -258,6 +258,8 @@ namespace osu.Game { dependencies.CacheAs(this); + SentryLogger.AttachUser(API.LocalUser); + dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 }); // bind config int to database RulesetInfo diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3f0610e9aa..324fcada89 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -125,7 +125,7 @@ namespace osu.Game protected MusicController MusicController { get; private set; } - protected internal IAPIProvider API { get; protected set; } + protected IAPIProvider API { get; set; } protected Storage Storage { get; set; } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 5cecc4d776..92f2902c0e 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; +using System.Diagnostics; using System.IO; using System.Net; -using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API.Requests.Responses; @@ -19,10 +21,9 @@ namespace osu.Game.Utils { private SentryClient sentry; private Scope sentryScope; - private Exception lastException; + private Exception? lastException; - [UsedImplicitly] - private readonly IBindable localUser; + private IBindable? localUser; public SentryLogger(OsuGame game) { @@ -40,16 +41,21 @@ namespace osu.Game.Utils sentryScope = new Scope(options); Logger.NewEntry += processLogEntry; + } - localUser = game.API.LocalUser.GetBoundCopy(); - localUser.BindValueChanged(user => + public void AttachUser(IBindable user) + { + Debug.Assert(localUser == null); + + localUser = user.GetBoundCopy(); + localUser.BindValueChanged(u => { sentryScope.User = new User { - Username = user.NewValue.Username, - Id = user.NewValue.Id.ToString(), + Username = u.NewValue.Username, + Id = u.NewValue.Id.ToString(), }; - }); + }, true); } private void processLogEntry(LogEntry entry) @@ -137,8 +143,6 @@ namespace osu.Game.Utils protected virtual void Dispose(bool isDisposing) { Logger.NewEntry -= processLogEntry; - sentry = null; - sentryScope = null; } #endregion From 9734d778f4bda85e2f38cd03e326c09cb3c40ac7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:44:54 +0900 Subject: [PATCH 20/76] Update sentry SDK usage in line with more recent specifications --- osu.Game/Utils/SentryLogger.cs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 92f2902c0e..02bcdd281a 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -19,26 +19,24 @@ namespace osu.Game.Utils /// public class SentryLogger : IDisposable { - private SentryClient sentry; - private Scope sentryScope; private Exception? lastException; private IBindable? localUser; + private readonly IDisposable? sentrySession; + public SentryLogger(OsuGame game) { - if (!game.IsDeployedBuild) return; - - var options = new SentryOptions + sentrySession = SentrySdk.Init(options => { - Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2", - AutoSessionTracking = true, - IsEnvironmentUser = false, - Release = game.Version - }; + // Not setting the dsn will completely disable sentry. + if (game.IsDeployedBuild) + options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2"; - sentry = new SentryClient(options); - sentryScope = new Scope(options); + options.AutoSessionTracking = true; + options.IsEnvironmentUser = false; + options.Release = game.Version; + }); Logger.NewEntry += processLogEntry; } @@ -50,11 +48,11 @@ namespace osu.Game.Utils localUser = user.GetBoundCopy(); localUser.BindValueChanged(u => { - sentryScope.User = new User + SentrySdk.ConfigureScope(scope => scope.User = new User { Username = u.NewValue.Username, Id = u.NewValue.Id.ToString(), - }; + }); }, true); } @@ -73,14 +71,14 @@ namespace osu.Game.Utils lastException = exception; - sentry.CaptureEvent(new SentryEvent(exception) + SentrySdk.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, Level = getSentryLevel(entry.Level), - }, sentryScope); + }); } else - sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation"); + SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation"); } private SentryLevel? getSentryLevel(LogLevel entryLevel) @@ -143,6 +141,7 @@ namespace osu.Game.Utils protected virtual void Dispose(bool isDisposing) { Logger.NewEntry -= processLogEntry; + sentrySession?.Dispose(); } #endregion From 99e6d56508799132912238edf898df000f33033a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 14:45:55 +0900 Subject: [PATCH 21/76] Add finalizer to sentry logger for safety --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 02bcdd281a..c6429b6b2c 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -41,6 +41,8 @@ namespace osu.Game.Utils Logger.NewEntry += processLogEntry; } + ~SentryLogger() => Dispose(false); + public void AttachUser(IBindable user) { Debug.Assert(localUser == null); From c6112b3ae78becb6980053ae047258c103a4db8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:07:02 +0900 Subject: [PATCH 22/76] Add unhandled exception marking --- osu.Game/Utils/SentryLogger.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index c6429b6b2c..728d6ab9be 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Online.API.Requests.Responses; using Sentry; +using Sentry.Protocol; namespace osu.Game.Utils { @@ -73,6 +74,13 @@ namespace osu.Game.Utils lastException = exception; + // framework does some weird exception redirection which means sentry does not see unhandled exceptions using its automatic methods. + // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. + // easiest solution is to check the message matches what the framework logs this as. + // see https://github.com/ppy/osu-framework/blob/f932f8df053f0011d755c95ad9a2ed61b94d136b/osu.Framework/Platform/GameHost.cs#L336 + bool wasHandled = entry.Message != @"An unhandled exception has occurred."; + exception.Data[Mechanism.HandledKey] = wasHandled; + SentrySdk.CaptureEvent(new SentryEvent(exception) { Message = entry.Message, From 363643a16d64777ffd0daf681507ec0e53d003c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:08:49 +0900 Subject: [PATCH 23/76] Remove sentry logger debounce This is probably going to result in a high quantity of exceptions, but I think this is fine. We can add rules as we go to not log certain exception types. --- osu.Game/Utils/SentryLogger.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 728d6ab9be..170d8e7cb0 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -20,8 +20,6 @@ namespace osu.Game.Utils /// public class SentryLogger : IDisposable { - private Exception? lastException; - private IBindable? localUser; private readonly IDisposable? sentrySession; @@ -69,11 +67,6 @@ namespace osu.Game.Utils { if (!shouldSubmitException(exception)) return; - // since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports. - if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return; - - lastException = exception; - // framework does some weird exception redirection which means sentry does not see unhandled exceptions using its automatic methods. // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. // easiest solution is to check the message matches what the framework logs this as. From 216c68e6d018246a37870fa2ada898f7def2a080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 15:19:43 +0900 Subject: [PATCH 24/76] Add unobserved exception hinting --- osu.Game/Utils/SentryLogger.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 170d8e7cb0..96affd85a7 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -71,8 +71,22 @@ namespace osu.Game.Utils // but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes. // easiest solution is to check the message matches what the framework logs this as. // see https://github.com/ppy/osu-framework/blob/f932f8df053f0011d755c95ad9a2ed61b94d136b/osu.Framework/Platform/GameHost.cs#L336 - bool wasHandled = entry.Message != @"An unhandled exception has occurred."; - exception.Data[Mechanism.HandledKey] = wasHandled; + bool wasUnhandled = entry.Message == @"An unhandled error has occurred."; + bool wasUnobserved = entry.Message == @"An unobserved error has occurred."; + + if (wasUnobserved) + { + // see https://github.com/getsentry/sentry-dotnet/blob/c6a660b1affc894441c63df2695a995701671744/src/Sentry/Integrations/TaskUnobservedTaskExceptionIntegration.cs#L39 + exception.Data[Mechanism.MechanismKey] = @"UnobservedTaskException"; + } + + if (wasUnhandled) + { + // see https://github.com/getsentry/sentry-dotnet/blob/main/src/Sentry/Integrations/AppDomainUnhandledExceptionIntegration.cs#L38-L39 + exception.Data[Mechanism.MechanismKey] = @"AppDomain.UnhandledException"; + } + + exception.Data[Mechanism.HandledKey] = !wasUnhandled; SentrySdk.CaptureEvent(new SentryEvent(exception) { From 6a49eb68759d89e4d001866a7ae4fcbb7addc8d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 16:14:04 +0900 Subject: [PATCH 25/76] Add breadcrumb level mappings --- osu.Game/Utils/SentryLogger.cs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 96affd85a7..ad4bcf6274 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -95,10 +95,31 @@ namespace osu.Game.Utils }); } else - SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation"); + SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation", level: getBreadcrumbLevel(entry.Level)); } - private SentryLevel? getSentryLevel(LogLevel entryLevel) + private BreadcrumbLevel getBreadcrumbLevel(LogLevel entryLevel) + { + switch (entryLevel) + { + case LogLevel.Debug: + return BreadcrumbLevel.Debug; + + case LogLevel.Verbose: + return BreadcrumbLevel.Info; + + case LogLevel.Important: + return BreadcrumbLevel.Warning; + + case LogLevel.Error: + return BreadcrumbLevel.Error; + + default: + throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null); + } + } + + private SentryLevel getSentryLevel(LogLevel entryLevel) { switch (entryLevel) { From 14a21e92244030a6f1fa758237f9733776b0c6b7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 May 2022 11:41:41 +0300 Subject: [PATCH 26/76] Workaround interface mocks in `TestSceneFirstRunSetupOverlay` breaking with hot reload --- .../TestSceneFirstRunSetupOverlay.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 39298f56ba..905f53c165 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Moq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -25,9 +27,9 @@ namespace osu.Game.Tests.Visual.UserInterface { private FirstRunSetupOverlay overlay; - private readonly Mock performer = new Mock(); + private readonly Mock performer = new Mock(); - private readonly Mock notificationOverlay = new Mock(); + private readonly Mock notificationOverlay = new Mock(); private Notification lastNotification; @@ -37,8 +39,8 @@ namespace osu.Game.Tests.Visual.UserInterface private void load() { Dependencies.Cache(LocalConfig = new OsuConfigManager(LocalStorage)); - Dependencies.CacheAs(performer.Object); - Dependencies.CacheAs(notificationOverlay.Object); + Dependencies.CacheAs(performer.Object); + Dependencies.CacheAs(notificationOverlay.Object); } [SetUpSteps] @@ -196,5 +198,31 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("overlay shown", () => overlay.State.Value == Visibility.Visible); AddAssert("is resumed", () => overlay.CurrentScreen is ScreenBeatmaps); } + + // interface mocks break hot reload, mocking this stub implementation instead works around it. + // see: https://github.com/moq/moq4/issues/1252 + [UsedImplicitly] + public class TestNotificationOverlay : INotificationOverlay + { + public virtual void Post(Notification notification) + { + } + + public virtual void Hide() + { + } + + public virtual IBindable UnreadCount => null; + } + + // interface mocks break hot reload, mocking this stub implementation instead works around it. + // see: https://github.com/moq/moq4/issues/1252 + [UsedImplicitly] + public class TestPerformerFromScreenRunner : IPerformFromScreenRunner + { + public virtual void PerformFromScreen(Action action, IEnumerable validScreens = null) + { + } + } } } From 9aadc274bfe5578d2dabe2388ee9f3af4928addd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 16:43:38 +0900 Subject: [PATCH 27/76] Show first run dialog on first run of the game --- .../Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs | 1 - osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 905f53c165..48b5690243 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -74,7 +74,6 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - [Ignore("Enable when first run setup is being displayed on first run.")] public void TestDoesntOpenOnSecondRun() { AddStep("set first run", () => LocalConfig.SetValue(OsuSetting.ShowFirstRunSetup, true)); diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 607bef76dd..df1e094114 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -171,8 +171,7 @@ namespace osu.Game.Overlays config.BindWith(OsuSetting.ShowFirstRunSetup, showFirstRunSetup); - // TODO: uncomment when happy with the whole flow. - // if (showFirstRunSetup.Value) Show(); + if (showFirstRunSetup.Value) Show(); } public override bool OnPressed(KeyBindingPressEvent e) @@ -304,8 +303,7 @@ namespace osu.Game.Overlays } else { - // TODO: uncomment when happy with the whole flow. - // showFirstRunSetup.Value = false; + showFirstRunSetup.Value = false; currentStepIndex = null; Hide(); } From bcce9c5e6772911ff817216c4424ad0ff596e7a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:01:36 +0900 Subject: [PATCH 28/76] Limit the width of first run overlay content --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 71 ++++++++++++++--------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index df1e094114..befa80e839 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays typeof(ScreenBehaviour), }; - private Container stackContainer = null!; + private Container screenContent = null!; private Bindable? overlayActivationMode; @@ -86,36 +86,51 @@ namespace osu.Game.Overlays Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + Padding = new MarginPadding { Bottom = 20, }, + Child = new GridContainer() { - Horizontal = 70 * 1.2f, - Bottom = 20, - }, - Child = new InputBlockingContainer - { - Masking = true, - CornerRadius = 14, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Children = new Drawable[] + ColumnDimensions = new[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background6, - }, - stackContainer = new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 20, - Horizontal = 70, - }, - } + new Dimension(), + new Dimension(minSize: 640, maxSize: 800), + new Dimension(), }, - }, + Content = new[] + { + new[] + { + Empty(), + new InputBlockingContainer + { + Masking = true, + CornerRadius = 14, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background6, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Vertical = 20, + Horizontal = 20, + }, + Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, }, + }, + }, + }, + Empty(), + }, + } + } }, }); @@ -268,7 +283,7 @@ namespace osu.Game.Overlays { Debug.Assert(currentStepIndex == null); - stackContainer.Child = stack = new ScreenStack + screenContent.Child = stack = new ScreenStack { RelativeSizeAxes = Axes.Both, }; From e5204e565dae9946543ca23c72119be7f26e0540 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:23:12 +0900 Subject: [PATCH 29/76] Move horizontal padding to content level to better align scrollbar --- .../FirstRunSetup/FirstRunSetupScreen.cs | 34 +++++++++++-------- osu.Game/Overlays/FirstRunSetupOverlay.cs | 6 +--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 1f18d181cb..2990948199 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -33,23 +33,29 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuScrollContainer(Direction.Vertical) { RelativeSizeAxes = Axes.Both, - ScrollbarOverlapsContent = false, - Children = new Drawable[] + Masking = false, + Child = new Container { - new OsuSpriteText + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 30 }, + Children = new Drawable[] { - Text = this.GetLocalisableDescription(), - Font = OsuFont.Default.With(size: header_size), - Colour = OverlayColourProvider.Light1, + new OsuSpriteText + { + Text = this.GetLocalisableDescription(), + Font = OsuFont.Default.With(size: header_size), + Colour = OverlayColourProvider.Light1, + }, + Content = new FillFlowContainer + { + Y = header_size + spacing, + Spacing = new Vector2(spacing), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + } }, - Content = new FillFlowContainer - { - Y = header_size + spacing, - Spacing = new Vector2(spacing), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - } }, } }; diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index befa80e839..19a4c09473 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -118,11 +118,7 @@ namespace osu.Game.Overlays new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 20, - Horizontal = 20, - }, + Padding = new MarginPadding { Vertical = 20 }, Child = screenContent = new Container { RelativeSizeAxes = Axes.Both, }, }, }, From 61313b69ecd40049dbc13b502e21a0e4cbeb312d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:33:37 +0900 Subject: [PATCH 30/76] Standardise font sizes in first run overlay screens --- .../Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 9 ++++++--- osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs | 14 +++++++------- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 2 +- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 2 +- osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 2990948199..2cfa7cd164 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -19,13 +19,16 @@ namespace osu.Game.Overlays.FirstRunSetup protected FillFlowContainer Content { get; private set; } + protected const float CONTENT_FONT_SIZE = 16; + + protected const float HEADER_FONT_SIZE = 24; + [Resolved] protected OverlayColourProvider OverlayColourProvider { get; private set; } [BackgroundDependencyLoader] private void load() { - const float header_size = 40; const float spacing = 20; InternalChildren = new Drawable[] @@ -44,12 +47,12 @@ namespace osu.Game.Overlays.FirstRunSetup new OsuSpriteText { Text = this.GetLocalisableDescription(), - Font = OsuFont.Default.With(size: header_size), + Font = OsuFont.TorusAlternate.With(size: HEADER_FONT_SIZE), Colour = OverlayColourProvider.Light1, }, Content = new FillFlowContainer { - Y = header_size + spacing, + Y = HEADER_FONT_SIZE + spacing, Spacing = new Vector2(spacing), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs index 190a0badab..66acdca8c7 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBeatmaps.cs @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.FirstRunSetup [BackgroundDependencyLoader(permitNulls: true)] private void load(LegacyImportManager? legacyImportManager) { - Vector2 buttonSize = new Vector2(500, 60); + Vector2 buttonSize = new Vector2(400, 50); Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.Description, @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.FirstRunSetup Height = 30, Children = new Drawable[] { - currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24, weight: FontWeight.SemiBold)) + currentlyLoadedBeatmaps = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: HEADER_FONT_SIZE, weight: FontWeight.SemiBold)) { Colour = OverlayColourProvider.Content2, TextAnchor = Anchor.Centre, @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.FirstRunSetup }, } }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.TutorialDescription, @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.FirstRunSetup Text = FirstRunSetupBeatmapScreenStrings.TutorialButton, Action = downloadTutorial }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.BundledDescription, @@ -105,7 +105,7 @@ namespace osu.Game.Overlays.FirstRunSetup Text = FirstRunSetupBeatmapScreenStrings.BundledButton, Action = downloadBundled }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = "If you have an existing osu! install, you can also choose to import your existing beatmap collection.", @@ -131,7 +131,7 @@ namespace osu.Game.Overlays.FirstRunSetup })); } }, - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Colour = OverlayColourProvider.Content1, Text = FirstRunSetupBeatmapScreenStrings.ObtainMoreBeatmaps, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index dc3d40ad95..9d426bd3fa 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.BehaviourDescription, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 152d67ab27..24e113e6a9 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 24)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.UIScaleDescription, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs index 10e15a7555..420d630857 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenWelcome.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.FirstRunSetup { Content.Children = new Drawable[] { - new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 20)) + new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) { Text = FirstRunSetupOverlayStrings.WelcomeDescription, RelativeSizeAxes = Axes.X, From 63b9e01d384eb8fda2c5439ab98807b1be7244e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:35:43 +0900 Subject: [PATCH 31/76] Fix behaviour screen using old style buttons --- osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs index 9d426bd3fa..1a88e6a842 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenBehaviour.cs @@ -9,7 +9,7 @@ using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.FirstRunSetup private SearchContainer searchContainer; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Content.Children = new Drawable[] { @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.FirstRunSetup { new[] { - new TriangleButton + new RoundedButton { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, @@ -59,10 +59,11 @@ namespace osu.Game.Overlays.FirstRunSetup Action = applyStandard, }, Empty(), - new DangerousTriangleButton + new RoundedButton { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + BackgroundColour = colours.Pink3, Text = FirstRunSetupOverlayStrings.ClassicDefaults, RelativeSizeAxes = Axes.X, Action = applyClassic From 493798ae5e5e4ba61e5654b2623eab4a249af750 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:44:23 +0900 Subject: [PATCH 32/76] Fix nested ui scale example screens no longer fitting --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 24e113e6a9..4a44a6d391 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -35,6 +35,8 @@ namespace osu.Game.Overlays.FirstRunSetup [BackgroundDependencyLoader] private void load(OsuConfigManager config) { + const float screen_width = 640; + Content.Children = new Drawable[] { new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: CONTENT_FONT_SIZE)) @@ -54,7 +56,7 @@ namespace osu.Game.Overlays.FirstRunSetup Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.None, - Size = new Vector2(960, 960 / 16f * 9 / 2), + Size = new Vector2(screen_width, screen_width / 16f * 9 / 2), Children = new Drawable[] { new GridContainer From 25c6226ca9cb7c82ef9a66e4c12f45f13ec7e732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 17:47:04 +0900 Subject: [PATCH 33/76] Adjust transition length for a more seamless screen change --- osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs index 2cfa7cd164..d1ea91e51a 100644 --- a/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs +++ b/osu.Game/Overlays/FirstRunSetup/FirstRunSetupScreen.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.FirstRunSetup { base.OnEntering(e); this - .FadeInFromZero(500) + .FadeInFromZero(100) .MoveToX(offset) .MoveToX(0, 500, Easing.OutQuint); } @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.FirstRunSetup { base.OnResuming(e); this - .FadeInFromZero(500) + .FadeInFromZero(100) .MoveToX(0, 500, Easing.OutQuint); } From a93c63b2b5b7d0119b3fb5ee97bee8232d593854 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 18:04:10 +0900 Subject: [PATCH 34/76] Add loading spinner when loading first run screens --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 19a4c09473..c0eed14c99 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -68,6 +69,9 @@ namespace osu.Game.Overlays private Container content = null!; + private LoadingSpinner loading = null!; + private ScheduledDelegate? loadingShowDelegate; + public FirstRunSetupOverlay() : base(OverlayColourScheme.Purple) { @@ -115,6 +119,7 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background6, }, + loading = new LoadingSpinner(), new Container { RelativeSizeAxes = Axes.Both, @@ -310,7 +315,16 @@ namespace osu.Game.Overlays if (currentStepIndex < steps.Length) { - stack.Push((Screen)Activator.CreateInstance(steps[currentStepIndex.Value])); + var nextScreen = (Screen)Activator.CreateInstance(steps[currentStepIndex.Value]); + + loadingShowDelegate = Scheduler.AddDelayed(() => loading.Show(), 200); + nextScreen.OnLoadComplete += _ => + { + loadingShowDelegate?.Cancel(); + loading.Hide(); + }; + + stack.Push(nextScreen); } else { From a578f7a406a049f8548af177b630c89a6ba32c8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 18:08:17 +0900 Subject: [PATCH 35/76] Force nested screens to load synchronously --- osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs | 15 +++++++++------ osu.Game/Screens/OsuScreenStack.cs | 7 +++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 4a44a6d391..8452691bb5 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -125,6 +125,7 @@ namespace osu.Game.Overlays.FirstRunSetup private class SampleScreenContainer : CompositeDrawable { + private readonly OsuScreen screen; // Minimal isolation from main game. [Cached] @@ -144,6 +145,12 @@ namespace osu.Game.Overlays.FirstRunSetup public override bool PropagatePositionalInputSubTree => false; public override bool PropagateNonPositionalInputSubTree => false; + public SampleScreenContainer(OsuScreen screen) + { + this.screen = screen; + RelativeSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] private void load(AudioManager audio, TextureStore textures, RulesetStore rulesets) { @@ -151,13 +158,8 @@ namespace osu.Game.Overlays.FirstRunSetup Beatmap.Value.LoadTrack(); Ruleset.Value = rulesets.AvailableRulesets.First(); - } - public SampleScreenContainer(Screen screen) - { OsuScreenStack stack; - RelativeSizeAxes = Axes.Both; - OsuLogo logo; Padding = new MarginPadding(5); @@ -191,7 +193,8 @@ namespace osu.Game.Overlays.FirstRunSetup }, }; - stack.Push(screen); + // intentionally load synchronously so it is included in the initial load of the first run screen. + stack.PushSynchronously(screen); } } } diff --git a/osu.Game/Screens/OsuScreenStack.cs b/osu.Game/Screens/OsuScreenStack.cs index ebbcbd7650..18b16ba865 100644 --- a/osu.Game/Screens/OsuScreenStack.cs +++ b/osu.Game/Screens/OsuScreenStack.cs @@ -29,6 +29,13 @@ namespace osu.Game.Screens ScreenExited += ScreenChanged; } + public void PushSynchronously(OsuScreen screen) + { + LoadComponent(screen); + + Push(screen); + } + private void screenPushed(IScreen prev, IScreen next) { if (LoadState < LoadState.Ready) From cac6d5569c8dbd1282330ffadf5a2d20bb0d0cb5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:07:00 +0900 Subject: [PATCH 36/76] Fix incorrect variable reference in log output --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index c5b69a3637..3a35bce05c 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -524,7 +524,7 @@ namespace osu.Game { bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; - Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {SoftHandledExceptions} more allowable exceptions" : "denied")} ."); + Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} ."); // restore the stock of allowable exceptions after a short delay. Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions)); From 4d22f262667972c4bf20cf1c117d8a7e1e5f2f45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:07:07 +0900 Subject: [PATCH 37/76] Rename property and improve xmldoc --- osu.Game.Tournament/TournamentGame.cs | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/OsuGameBase.cs | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index d9db0e6d20..042e660122 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; - protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 6961dce910..0cfb3c2de8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -176,7 +176,7 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); - protected override int SoftHandledExceptions => DebugUtils.IsDebugBuild ? 0 : 1; + protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; public OsuGame(string[] args = null) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3a35bce05c..b17526114a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -182,15 +182,20 @@ namespace osu.Game protected DatabaseContextFactory EFContextFactory { get; private set; } /// - /// Number of exceptions to allow before aborting execution. + /// Number of unhandled exceptions to allow before aborting execution. /// - protected virtual int SoftHandledExceptions => 0; + /// + /// When an unhandled exception is encountered, an internal count will be decremented. + /// If the count hits zero, the game will crash. + /// Each second, the count is incremented until reaching the value specified. + /// + protected virtual int ExceptionsBeforeCrash => 0; public OsuGameBase() { Name = @"osu!"; - allowableExceptions = SoftHandledExceptions; + allowableExceptions = ExceptionsBeforeCrash; } [BackgroundDependencyLoader] From b2a57c34bbea091d2e172c03a05c9481e9312bc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 19:10:34 +0900 Subject: [PATCH 38/76] Move implementation to base --- osu.Game.Tournament/TournamentGame.cs | 3 --- osu.Game/OsuGame.cs | 3 --- osu.Game/OsuGameBase.cs | 4 ++-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 042e660122..7967f54b49 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; -using osu.Framework.Development; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -40,8 +39,6 @@ namespace osu.Game.Tournament private Bindable windowMode; private LoadingSpinner loadingSpinner; - protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; - [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig, GameHost host) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0cfb3c2de8..54c4231b06 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,7 +13,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; -using osu.Framework.Development; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -176,8 +175,6 @@ namespace osu.Game private readonly List visibleBlockingOverlays = new List(); - protected override int ExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; - public OsuGame(string[] args = null) { this.args = args; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b17526114a..ce798d4027 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -189,13 +189,13 @@ namespace osu.Game /// If the count hits zero, the game will crash. /// Each second, the count is incremented until reaching the value specified. /// - protected virtual int ExceptionsBeforeCrash => 0; + protected virtual int UnhandledExceptionsBeforeCrash => DebugUtils.IsDebugBuild ? 0 : 1; public OsuGameBase() { Name = @"osu!"; - allowableExceptions = ExceptionsBeforeCrash; + allowableExceptions = UnhandledExceptionsBeforeCrash; } [BackgroundDependencyLoader] From 42fe7082250654233c6c78074a61cfbfec0866fb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 10 May 2022 19:56:21 +0900 Subject: [PATCH 39/76] Fix inspection --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index c0eed14c99..cebb2f5e3b 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Bottom = 20, }, - Child = new GridContainer() + Child = new GridContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, From 87f6f74795beb592dd50e5994f2f48bca590eac6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 21:03:55 +0900 Subject: [PATCH 40/76] Add failing test coverage of adding a file to a detached beatmap across threads --- .../Database/BeatmapImporterTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index f9c13a8169..b7bfe14402 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -136,6 +136,37 @@ namespace osu.Game.Tests.Database }); } + [Test] + public void TestAddFileToAsyncImportedBeatmap() + { + RunTestWithRealm((realm, storage) => + { + BeatmapSetInfo? detachedSet = null; + + using (var importer = new BeatmapModelManager(realm, storage)) + using (new RealmRulesetStore(realm, storage)) + { + Task.Run(async () => + { + Live? beatmapSet; + + using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream())) + // ReSharper disable once AccessToDisposedClosure + beatmapSet = await importer.Import(reader); + + Assert.NotNull(beatmapSet); + Debug.Assert(beatmapSet != null); + + // Intentionally detach on async thread as to not trigger a refresh on the main thread. + beatmapSet.PerformRead(s => detachedSet = s.Detach()); + }).WaitSafely(); + + Debug.Assert(detachedSet != null); + importer.AddFile(detachedSet, new MemoryStream(), "test"); + } + }); + } + [Test] public void TestImportBeatmapThenCleanup() { From 33f024212ffaaa1d70c38b20ff46e42b77fd8654 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 May 2022 20:47:26 +0900 Subject: [PATCH 41/76] Fix realm refetch operations potentially being unsafe As seen in test failure https://github.com/ppy/osu/runs/6357384721?check_suite_focus=true. --- osu.Game/Database/RealmAccess.cs | 20 +++++++++++++++++ osu.Game/Stores/RealmArchiveModelManager.cs | 25 ++++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index b0a70b51d0..937876a70e 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -344,6 +344,26 @@ namespace osu.Game.Database } } + /// + /// Write changes to realm. + /// + /// The work to run. + public T Write(Func action) + { + if (ThreadSafety.IsUpdateThread) + { + total_writes_update.Value++; + return Realm.Write(action); + } + else + { + total_writes_async.Value++; + + using (var realm = getRealmInstance()) + return realm.Write(action); + } + } + /// /// Write changes to realm. /// diff --git a/osu.Game/Stores/RealmArchiveModelManager.cs b/osu.Game/Stores/RealmArchiveModelManager.cs index 57e51b79aa..e349efe5ad 100644 --- a/osu.Game/Stores/RealmArchiveModelManager.cs +++ b/osu.Game/Stores/RealmArchiveModelManager.cs @@ -45,11 +45,16 @@ namespace osu.Game.Stores // This method should be removed as soon as all the surrounding pieces support non-detached operations. if (!item.IsManaged) { - var managed = Realm.Realm.Find(item.ID); - managed.Realm.Write(() => operation(managed)); + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + Realm.Realm.Write(realm => + { + var managed = Realm.Realm.Find(item.ID); + operation(managed); - item.Files.Clear(); - item.Files.AddRange(managed.Files.Detach()); + item.Files.Clear(); + item.Files.AddRange(managed.Files.Detach()); + }); } else operation(item); @@ -165,7 +170,9 @@ namespace osu.Game.Stores public bool Delete(TModel item) { - return Realm.Run(realm => + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + return Realm.Write(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -173,14 +180,16 @@ namespace osu.Game.Stores if (item?.DeletePending != false) return false; - realm.Write(r => item.DeletePending = true); + item.DeletePending = true; return true; }); } public void Undelete(TModel item) { - Realm.Run(realm => + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + Realm.Write(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -188,7 +197,7 @@ namespace osu.Game.Stores if (item?.DeletePending != true) return; - realm.Write(r => item.DeletePending = false); + item.DeletePending = false; }); } From ec231e0f312eeeecb8229a0fe1ee0660935b455f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 00:45:17 +0900 Subject: [PATCH 42/76] Use more local realm reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Stores/RealmArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Stores/RealmArchiveModelManager.cs b/osu.Game/Stores/RealmArchiveModelManager.cs index e349efe5ad..cc8229b436 100644 --- a/osu.Game/Stores/RealmArchiveModelManager.cs +++ b/osu.Game/Stores/RealmArchiveModelManager.cs @@ -49,7 +49,7 @@ namespace osu.Game.Stores // (ie. if an async import finished very recently). Realm.Realm.Write(realm => { - var managed = Realm.Realm.Find(item.ID); + var managed = realm.Find(item.ID); operation(managed); item.Files.Clear(); From 9cfe2cc310b916f2ca78ca1daefd256a777f2dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:43:57 +0200 Subject: [PATCH 43/76] Move `TestCustomisableModRuleset` out of `TestSceneModSettings` --- .../Mods/TestCustomisableModRuleset.cs | 80 +++++++++++++++++++ .../Gameplay/TestSceneReplayRecorder.cs | 6 +- .../Gameplay/TestSceneSpectatorPlayback.cs | 6 +- .../UserInterface/TestSceneModSettings.cs | 78 +----------------- 4 files changed, 90 insertions(+), 80 deletions(-) create mode 100644 osu.Game.Tests/Mods/TestCustomisableModRuleset.cs diff --git a/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs b/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs new file mode 100644 index 0000000000..3992d9abe6 --- /dev/null +++ b/osu.Game.Tests/Mods/TestCustomisableModRuleset.cs @@ -0,0 +1,80 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Tests.Mods +{ + public class TestCustomisableModRuleset : Ruleset + { + public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo; + + public override IEnumerable GetModsFor(ModType type) + { + if (type == ModType.Conversion) + { + return new Mod[] + { + new TestModCustomisable1(), + new TestModCustomisable2() + }; + } + + return Array.Empty(); + } + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); + + public override string Description { get; } = "test"; + public override string ShortName { get; } = "tst"; + + public class TestModCustomisable1 : TestModCustomisable + { + public override string Name => "Customisable Mod 1"; + + public override string Acronym => "CM1"; + } + + public class TestModCustomisable2 : TestModCustomisable + { + public override string Name => "Customisable Mod 2"; + + public override string Acronym => "CM2"; + + public override bool RequiresConfiguration => true; + } + + public abstract class TestModCustomisable : Mod, IApplicableMod + { + public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a customisable test mod."; + + public override ModType Type => ModType.Conversion; + + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Value = 7 + }; + + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); + } + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs index 8df32c500e..81763564fa 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayRecorder.cs @@ -24,7 +24,7 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; -using osu.Game.Tests.Visual.UserInterface; +using osu.Game.Tests.Mods; using osuTK; using osuTK.Graphics; using osuTK.Input; @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay { new Drawable[] { - recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + recordingManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Recorder = recorder = new TestReplayRecorder(new Score { @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + playbackManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { ReplayInputHandler = new TestFramedReplayInputHandler(replay) { diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 4ec46036f6..f8748922cf 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -27,8 +27,8 @@ using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Play; +using osu.Game.Tests.Mods; using osu.Game.Tests.Visual.Spectator; -using osu.Game.Tests.Visual.UserInterface; using osuTK; using osuTK.Graphics; @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay { new Drawable[] { - recordingManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + recordingManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Recorder = recorder = new TestReplayRecorder { @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - playbackManager = new TestRulesetInputManager(TestSceneModSettings.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) + playbackManager = new TestRulesetInputManager(TestCustomisableModRuleset.CreateTestRulesetInfo(), 0, SimultaneousBindingMode.Unique) { Clock = new FramedClock(manualClock), ReplayInputHandler = replayHandler = new TestFramedReplayInputHandler(replay) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 9a3083e8db..379735a7f5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -5,20 +5,15 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.UI; +using osu.Game.Tests.Mods; using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface @@ -27,15 +22,15 @@ namespace osu.Game.Tests.Visual.UserInterface { private TestModSelectOverlay modSelect; - private readonly Mod testCustomisableMod = new TestModCustomisable1(); + private readonly Mod testCustomisableMod = new TestCustomisableModRuleset.TestModCustomisable1(); - private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2(); + private readonly Mod testCustomisableAutoOpenMod = new TestCustomisableModRuleset.TestModCustomisable2(); [SetUp] public void SetUp() => Schedule(() => { SelectedMods.Value = Array.Empty(); - Ruleset.Value = CreateTestRulesetInfo(); + Ruleset.Value = TestCustomisableModRuleset.CreateTestRulesetInfo(); }); [Test] @@ -169,70 +164,5 @@ namespace osu.Game.Tests.Visual.UserInterface public void SetModSettingsWidth(float newWidth) => ModSettingsContainer.Parent.Width = newWidth; } - - public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo; - - public class TestCustomisableModRuleset : Ruleset - { - public override IEnumerable GetModsFor(ModType type) - { - if (type == ModType.Conversion) - { - return new Mod[] - { - new TestModCustomisable1(), - new TestModCustomisable2() - }; - } - - return Array.Empty(); - } - - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException(); - - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); - - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException(); - - public override string Description { get; } = "test"; - public override string ShortName { get; } = "tst"; - } - - private class TestModCustomisable1 : TestModCustomisable - { - public override string Name => "Customisable Mod 1"; - - public override string Acronym => "CM1"; - } - - private class TestModCustomisable2 : TestModCustomisable - { - public override string Name => "Customisable Mod 2"; - - public override string Acronym => "CM2"; - - public override bool RequiresConfiguration => true; - } - - private abstract class TestModCustomisable : Mod, IApplicableMod - { - public override double ScoreMultiplier => 1.0; - - public override string Description => "This is a customisable test mod."; - - public override ModType Type => ModType.Conversion; - - [SettingSource("Sample float", "Change something for a mod")] - public BindableFloat SliderBindable { get; } = new BindableFloat - { - MinValue = 0, - MaxValue = 10, - Default = 5, - Value = 7 - }; - - [SettingSource("Sample bool", "Clicking this changes a setting")] - public BindableBool TickBindable { get; } = new BindableBool(); - } } } From 4a3447f59f69af6b591f4c6c44f5b3422b229b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:45:57 +0200 Subject: [PATCH 44/76] Remove old free mod select overlay --- .../TestSceneFreeModSelectOverlay.cs | 21 --- .../OnlinePlay/FreeModSelectOverlay.cs | 157 ------------------ 2 files changed, 178 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs delete mode 100644 osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs deleted file mode 100644 index 26a0301d8a..0000000000 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ /dev/null @@ -1,21 +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.Framework.Graphics.Containers; -using osu.Game.Screens.OnlinePlay; - -namespace osu.Game.Tests.Visual.Multiplayer -{ - public class TestSceneFreeModSelectOverlay : MultiplayerTestScene - { - [SetUp] - public new void Setup() => Schedule(() => - { - Child = new FreeModSelectOverlay - { - State = { Value = Visibility.Visible } - }; - }); - } -} diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs deleted file mode 100644 index d5abaaab4e..0000000000 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Screens.OnlinePlay -{ - /// - /// A used for free-mod selection in online play. - /// - public class FreeModSelectOverlay : ModSelectOverlay - { - protected override bool Stacked => false; - - protected override bool AllowConfiguration => false; - - public new Func IsValidMod - { - get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value(m); - } - - public FreeModSelectOverlay() - { - IsValidMod = m => true; - - DeselectAllButton.Alpha = 0; - - Drawable selectAllButton; - Drawable deselectAllButton; - - FooterContainer.AddRange(new[] - { - selectAllButton = new TriangleButton - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Width = 180, - Text = "Select All", - Action = selectAll, - }, - // Unlike the base mod select overlay, this button deselects mods instantaneously. - deselectAllButton = new TriangleButton - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Width = 180, - Text = "Deselect All", - Action = deselectAll, - }, - }); - - FooterContainer.SetLayoutPosition(selectAllButton, -2); - FooterContainer.SetLayoutPosition(deselectAllButton, -1); - } - - private void selectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.SelectAll(); - } - - private void deselectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.DeselectAll(); - } - - protected override void OnAvailableModsChanged() - { - base.OnAvailableModsChanged(); - - foreach (var section in ModSectionsContainer.Children) - ((FreeModSection)section).UpdateCheckboxState(); - } - - protected override ModSection CreateModSection(ModType type) => new FreeModSection(type); - - private class FreeModSection : ModSection - { - private HeaderCheckbox checkbox; - - public FreeModSection(ModType type) - : base(type) - { - } - - protected override Drawable CreateHeader(string text) => new Container - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Child = checkbox = new HeaderCheckbox - { - LabelText = text, - Changed = onCheckboxChanged - } - }; - - private void onCheckboxChanged(bool value) - { - if (value) - SelectAll(); - else - DeselectAll(); - } - - protected override void ModButtonStateChanged(Mod mod) - { - base.ModButtonStateChanged(mod); - UpdateCheckboxState(); - } - - public void UpdateCheckboxState() - { - if (!SelectionAnimationRunning) - { - var validButtons = Buttons.Where(b => b.Mod.HasImplementation); - checkbox.Current.Value = validButtons.All(b => b.Selected); - } - } - } - - private class HeaderCheckbox : OsuCheckbox - { - public Action Changed; - - protected override bool PlaySoundsOnUserChange => false; - - public HeaderCheckbox() - : base(false) - - { - } - - protected override void ApplyLabelParameters(SpriteText text) - { - base.ApplyLabelParameters(text); - - text.Font = OsuFont.GetFont(weight: FontWeight.Bold); - } - - protected override void OnUserChange(bool value) - { - base.OnUserChange(value); - Changed?.Invoke(value); - } - } - } -} From 24c59e2f2fa942769843ebd8afd299c4008c3c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:48:21 +0200 Subject: [PATCH 45/76] Remove old user mod select overlay --- .../TestSceneModSelectOverlay.cs | 472 ------------------ .../UserInterface/TestSceneModSettings.cs | 119 ----- .../IncompatibilityDisplayingModButton.cs | 66 --- .../Overlays/Mods/UserModSelectOverlay.cs | 30 -- 4 files changed, 687 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs delete mode 100644 osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs delete mode 100644 osu.Game/Overlays/Mods/UserModSelectOverlay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs deleted file mode 100644 index b429619044..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens.Play.HUD; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.UserInterface -{ - [Description("mod select and icon display")] - public class TestSceneModSelectOverlay : OsuTestScene - { - private RulesetStore rulesets; - private ModDisplay modDisplay; - private TestModSelectOverlay modSelect; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - - [SetUp] - public void SetUp() => Schedule(() => - { - SelectedMods.Value = Array.Empty(); - createDisplay(() => new TestModSelectOverlay()); - }); - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("show", () => modSelect.Show()); - } - - /// - /// Ensure that two mod overlays are not cross polluting via central settings instances. - /// - [Test] - public void TestSettingsNotCrossPolluting() - { - Bindable> selectedMods2 = null; - - AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); - - AddStep("set setting", () => modSelect.ChildrenOfType>().First().Current.Value = 8); - - AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); - - AddStep("create second bindable", () => selectedMods2 = new Bindable>(new Mod[] { new OsuModDifficultyAdjust() })); - - AddStep("create second overlay", () => - { - Add(modSelect = new TestModSelectOverlay().With(d => - { - d.Origin = Anchor.TopCentre; - d.Anchor = Anchor.TopCentre; - d.SelectedMods.BindTarget = selectedMods2; - })); - }); - - AddStep("show", () => modSelect.Show()); - - AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); - AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); - } - - [Test] - public void TestSettingsResetOnDeselection() - { - var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } }; - - changeRuleset(0); - - AddStep("set dt mod with custom rate", () => { SelectedMods.Value = new[] { osuModDoubleTime }; }); - - AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2); - - AddStep("deselect", () => modSelect.DeselectAllButton.TriggerClick()); - AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0); - - AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).TriggerClick()); - AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true); - } - - [Test] - public void TestAnimationFlushOnClose() - { - changeRuleset(0); - - AddStep("Select all fun mods", () => - { - modSelect.ModSectionsContainer - .Single(c => c.ModType == ModType.DifficultyIncrease) - .SelectAll(); - }); - - AddUntilStep("many mods selected", () => modDisplay.Current.Value.Count >= 5); - - AddStep("trigger deselect and close overlay", () => - { - modSelect.ModSectionsContainer - .Single(c => c.ModType == ModType.DifficultyIncrease) - .DeselectAll(); - - modSelect.Hide(); - }); - - AddAssert("all mods deselected", () => modDisplay.Current.Value.Count == 0); - } - - [Test] - public void TestOsuMods() - { - changeRuleset(0); - - var osu = new OsuRuleset(); - - var easierMods = osu.GetModsFor(ModType.DifficultyReduction); - var harderMods = osu.GetModsFor(ModType.DifficultyIncrease); - - var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); - - var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); - - var easy = easierMods.FirstOrDefault(m => m is OsuModEasy); - var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock); - - testSingleMod(noFailMod); - testMultiMod(doubleTimeMod); - testIncompatibleMods(easy, hardRock); - testDeselectAll(easierMods.Where(m => !(m is MultiMod))); - } - - [Test] - public void TestManiaMods() - { - changeRuleset(3); - - var mania = new ManiaRuleset(); - - testModsWithSameBaseType( - mania.CreateMod(), - mania.CreateMod()); - } - - [Test] - public void TestRulesetChanges() - { - changeRuleset(0); - - var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); - - AddStep("set mods externally", () => { SelectedMods.Value = new[] { noFailMod }; }); - - changeRuleset(0); - - AddAssert("ensure mods still selected", () => modDisplay.Current.Value.SingleOrDefault(m => m is OsuModNoFail) != null); - - changeRuleset(3); - - AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); - - changeRuleset(0); - - AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); - } - - [Test] - public void TestExternallySetCustomizedMod() - { - changeRuleset(0); - - AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); - - AddAssert("ensure button is selected and customized accordingly", () => - { - var button = modSelect.GetModButton(SelectedMods.Value.Single()); - return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01; - }); - } - - [Test] - public void TestSettingsAreRetainedOnReload() - { - changeRuleset(0); - - AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); - - AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); - - AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay())); - - AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01); - } - - [Test] - public void TestExternallySetModIsReplacedByOverlayInstance() - { - Mod external = new OsuModDoubleTime(); - Mod overlayButtonMod = null; - - changeRuleset(0); - - AddStep("set mod externally", () => { SelectedMods.Value = new[] { external }; }); - - AddAssert("ensure button is selected", () => - { - var button = modSelect.GetModButton(SelectedMods.Value.Single()); - overlayButtonMod = button.SelectedMod; - return overlayButtonMod.GetType() == external.GetType(); - }); - - // Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own - AddAssert("mod instance doesn't match", () => external != overlayButtonMod); - - AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1); - AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod)); - AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external)); - } - - [Test] - public void TestNonStacked() - { - changeRuleset(0); - - AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay())); - - AddStep("show", () => modSelect.Show()); - - AddAssert("ensure all buttons are spread out", () => modSelect.ChildrenOfType().All(m => m.Mods.Length <= 1)); - } - - [Test] - public void TestChangeIsValidChangesButtonVisibility() - { - changeRuleset(0); - - AddAssert("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); - - AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime)); - AddUntilStep("double time not visible", () => modSelect.ChildrenOfType().All(b => !b.Mods.Any(m => m is OsuModDoubleTime))); - AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); - - AddStep("make double time valid again", () => modSelect.IsValidMod = m => true); - AddUntilStep("double time visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModDoubleTime))); - AddAssert("nightcore still visible", () => modSelect.ChildrenOfType().Any(b => b.Mods.Any(m => m is OsuModNightcore))); - } - - [Test] - public void TestChangeIsValidPreservesSelection() - { - changeRuleset(0); - - AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); - - AddStep("make NF invalid", () => modSelect.IsValidMod = m => !(m is ModNoFail)); - AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType().Count(b => b.Selected) == 2); - } - - [Test] - public void TestUnimplementedModIsUnselectable() - { - var testRuleset = new TestUnimplementedModOsuRuleset(); - changeTestRuleset(testRuleset.RulesetInfo); - - var conversionMods = testRuleset.GetModsFor(ModType.Conversion); - - var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod); - - testUnimplementedMod(unimplementedMod); - } - - private void testSingleMod(Mod mod) - { - selectNext(mod); - checkSelected(mod); - - selectPrevious(mod); - checkNotSelected(mod); - - selectNext(mod); - selectNext(mod); - checkNotSelected(mod); - - selectPrevious(mod); - selectPrevious(mod); - checkNotSelected(mod); - } - - private void testMultiMod(MultiMod multiMod) - { - foreach (var mod in multiMod.Mods) - { - selectNext(mod); - checkSelected(mod); - } - - for (int index = multiMod.Mods.Length - 1; index >= 0; index--) - selectPrevious(multiMod.Mods[index]); - - foreach (var mod in multiMod.Mods) - checkNotSelected(mod); - } - - private void testUnimplementedMod(Mod mod) - { - selectNext(mod); - checkNotSelected(mod); - } - - private void testIncompatibleMods(Mod modA, Mod modB) - { - selectNext(modA); - checkSelected(modA); - checkNotSelected(modB); - - selectNext(modB); - checkSelected(modB); - checkNotSelected(modA); - - selectPrevious(modB); - checkNotSelected(modA); - checkNotSelected(modB); - } - - private void testDeselectAll(IEnumerable mods) - { - foreach (var mod in mods) - selectNext(mod); - - AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any()); - AddStep("deselect all", () => modSelect.DeselectAllButton.Action.Invoke()); - AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any()); - } - - private void testModsWithSameBaseType(Mod modA, Mod modB) - { - selectNext(modA); - checkSelected(modA); - selectNext(modB); - checkSelected(modB); - - // Backwards - selectPrevious(modA); - checkSelected(modA); - } - - private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); - - private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); - - private void checkSelected(Mod mod) - { - AddAssert($"check {mod.Name} is selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.SingleOrDefault(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected; - }); - } - - private void changeRuleset(int? onlineId) - { - AddStep($"change ruleset to {(onlineId?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.OnlineID == onlineId); }); - waitForLoad(); - } - - private void changeTestRuleset(RulesetInfo rulesetInfo) - { - AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; }); - waitForLoad(); - } - - private void waitForLoad() => - AddUntilStep("wait for icons to load", () => modSelect.AllLoaded); - - private void checkNotSelected(Mod mod) - { - AddAssert($"check {mod.Name} is not selected", () => - { - var button = modSelect.GetModButton(mod); - return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType(); - }); - } - - private void createDisplay(Func createOverlayFunc) - { - Children = new Drawable[] - { - modSelect = createOverlayFunc().With(d => - { - d.Origin = Anchor.BottomCentre; - d.Anchor = Anchor.BottomCentre; - d.SelectedMods.BindTarget = SelectedMods; - }), - modDisplay = new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Position = new Vector2(-5, 25), - Current = { BindTarget = modSelect.SelectedMods } - } - }; - } - - private class TestModSelectOverlay : UserModSelectOverlay - { - public new Bindable> SelectedMods => base.SelectedMods; - - public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); - - public new FillFlowContainer ModSectionsContainer => - base.ModSectionsContainer; - - public ModButton GetModButton(Mod mod) - { - var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type); - return section.ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); - } - - public new TriangleButton DeselectAllButton => base.DeselectAllButton; - - public new Color4 LowMultiplierColour => base.LowMultiplierColour; - public new Color4 HighMultiplierColour => base.HighMultiplierColour; - } - - private class TestNonStackedModSelectOverlay : TestModSelectOverlay - { - protected override bool Stacked => false; - } - - private class TestUnimplementedMod : Mod - { - public override string Name => "Unimplemented mod"; - public override string Acronym => "UM"; - public override string Description => "A mod that is not implemented."; - public override double ScoreMultiplier => 1; - public override ModType Type => ModType.Conversion; - } - - private class TestUnimplementedModOsuRuleset : OsuRuleset - { - public override string ShortName => "unimplemented"; - - public override IEnumerable GetModsFor(ModType type) - { - if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() }); - - return base.GetModsFor(type); - } - } - } -} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 379735a7f5..1cf3b2651c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -1,79 +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; -using System.Collections.Generic; -using System.Linq; using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Utils; -using osu.Framework.Testing; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Tests.Mods; -using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { public class TestSceneModSettings : OsuManualInputManagerTestScene { - private TestModSelectOverlay modSelect; - - private readonly Mod testCustomisableMod = new TestCustomisableModRuleset.TestModCustomisable1(); - - private readonly Mod testCustomisableAutoOpenMod = new TestCustomisableModRuleset.TestModCustomisable2(); - - [SetUp] - public void SetUp() => Schedule(() => - { - SelectedMods.Value = Array.Empty(); - Ruleset.Value = TestCustomisableModRuleset.CreateTestRulesetInfo(); - }); - - [Test] - public void TestButtonShowsOnCustomisableMod() - { - createModSelect(); - openModSelect(); - - AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); - AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); - AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); - AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); - AddStep("open Customisation", () => modSelect.CustomiseButton.TriggerClick()); - AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod)); - AddAssert("controls hidden", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - } - - [Test] - public void TestButtonShowsOnModAlreadyAdded() - { - AddStep("set active mods", () => SelectedMods.Value = new List { testCustomisableMod }); - - createModSelect(); - - AddAssert("mods still active", () => SelectedMods.Value.Count == 1); - - openModSelect(); - AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); - } - - [Test] - public void TestCustomisationMenuVisibility() - { - createModSelect(); - openModSelect(); - - AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible); - AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden); - } - [Test] public void TestModSettingsUnboundWhenCopied() { @@ -109,60 +45,5 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value)); AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value)); } - - [Test] - public void TestCustomisationMenuNoClickthrough() - { - createModSelect(); - openModSelect(); - - AddStep("change mod settings menu width to full screen", () => modSelect.SetModSettingsWidth(1.0f)); - AddStep("select cm2", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); - AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible); - AddStep("hover over mod behind settings menu", () => InputManager.MoveMouseTo(modSelect.GetModButton(testCustomisableMod))); - AddAssert("Mod is not considered hovered over", () => !modSelect.GetModButton(testCustomisableMod).IsHovered); - AddStep("left click mod", () => InputManager.Click(MouseButton.Left)); - AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1); - AddStep("right click mod", () => InputManager.Click(MouseButton.Right)); - AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1); - } - - private void createModSelect() - { - AddStep("create mod select", () => - { - Child = modSelect = new TestModSelectOverlay - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - SelectedMods = { BindTarget = SelectedMods } - }; - }); - } - - private void openModSelect() - { - AddStep("open", () => modSelect.Show()); - AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded); - } - - private class TestModSelectOverlay : UserModSelectOverlay - { - public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer; - public new TriangleButton CustomiseButton => base.CustomiseButton; - - public bool ButtonsLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); - - public ModButton GetModButton(Mod mod) - { - return ModSectionsContainer.ChildrenOfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())); - } - - public void SelectMod(Mod mod) => - GetModButton(mod).SelectNext(1); - - public void SetModSettingsWidth(float newWidth) => - ModSettingsContainer.Parent.Width = newWidth; - } } } diff --git a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs b/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs deleted file mode 100644 index 6e2cb40596..0000000000 --- a/osu.Game/Overlays/Mods/IncompatibilityDisplayingModButton.cs +++ /dev/null @@ -1,66 +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.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Game.Rulesets.Mods; -using osu.Game.Utils; -using osuTK; - -namespace osu.Game.Overlays.Mods -{ - public class IncompatibilityDisplayingModButton : ModButton - { - private readonly CompositeDrawable incompatibleIcon; - - [Resolved] - private Bindable> selectedMods { get; set; } - - public IncompatibilityDisplayingModButton(Mod mod) - : base(mod) - { - ButtonContent.Add(incompatibleIcon = new IncompatibleIcon - { - Anchor = Anchor.BottomRight, - Origin = Anchor.Centre, - Position = new Vector2(-13), - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateCompatibility), true); - } - - protected override void DisplayMod(Mod mod) - { - base.DisplayMod(mod); - - Scheduler.AddOnce(updateCompatibility); - } - - private void updateCompatibility() - { - var m = SelectedMod ?? Mods.First(); - - bool isIncompatible = false; - - if (selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(m)) - isIncompatible = !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(m)); - - if (isIncompatible) - incompatibleIcon.Show(); - else - incompatibleIcon.Hide(); - } - - public override ITooltip GetCustomTooltip() => new IncompatibilityDisplayingTooltip(); - } -} diff --git a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs deleted file mode 100644 index 161f89c2eb..0000000000 --- a/osu.Game/Overlays/Mods/UserModSelectOverlay.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Overlays.Mods -{ - public class UserModSelectOverlay : ModSelectOverlay - { - protected override void OnModSelected(Mod mod) - { - base.OnModSelected(mod); - - foreach (var section in ModSectionsContainer.Children) - section.DeselectTypes(mod.IncompatibleMods, true, mod); - } - - protected override ModSection CreateModSection(ModType type) => new UserModSection(type); - - private class UserModSection : ModSection - { - public UserModSection(ModType type) - : base(type) - { - } - - protected override ModButton CreateModButton(Mod mod) => new IncompatibilityDisplayingModButton(mod); - } - } -} From 128468e13d87aa8f2ed54ddb4264732b96c2b1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:52:30 +0200 Subject: [PATCH 46/76] Remove old base mod select overlay --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 532 --------------------- 1 file changed, 532 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModSelectOverlay.cs diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs deleted file mode 100644 index cf57322594..0000000000 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -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.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Input.Bindings; -using osu.Game.Resources.Localisation.Web; -using osu.Game.Rulesets.Mods; -using osu.Game.Screens; -using osu.Game.Utils; -using osuTK; -using osuTK.Graphics; -using osuTK.Input; - -namespace osu.Game.Overlays.Mods -{ - public abstract class ModSelectOverlay : WaveOverlayContainer - { - public const float HEIGHT = 510; - - protected readonly TriangleButton DeselectAllButton; - protected readonly TriangleButton CustomiseButton; - protected readonly TriangleButton CloseButton; - - protected readonly FillFlowContainer FooterContainer; - - protected override bool BlockNonPositionalInput => false; - - protected override bool DimMainContent => false; - - /// - /// Whether s underneath the same instance should appear as stacked buttons. - /// - protected virtual bool Stacked => true; - - /// - /// Whether configurable s can be configured by the local user. - /// - protected virtual bool AllowConfiguration => true; - - [NotNull] - private Func isValidMod = m => true; - - /// - /// A function that checks whether a given mod is selectable. - /// - [NotNull] - public Func IsValidMod - { - get => isValidMod; - set - { - isValidMod = value ?? throw new ArgumentNullException(nameof(value)); - updateAvailableMods(); - } - } - - protected readonly FillFlowContainer ModSectionsContainer; - - protected readonly ModSettingsContainer ModSettingsContainer; - - [Cached] - public readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); - - private Bindable>> availableMods; - - protected Color4 LowMultiplierColour; - protected Color4 HighMultiplierColour; - - private const float content_width = 0.8f; - private const float footer_button_spacing = 20; - - private Sample sampleOn, sampleOff; - - protected ModSelectOverlay() - { - Waves.FirstWaveColour = Color4Extensions.FromHex(@"19b0e2"); - Waves.SecondWaveColour = Color4Extensions.FromHex(@"2280a2"); - Waves.ThirdWaveColour = Color4Extensions.FromHex(@"005774"); - Waves.FourthWaveColour = Color4Extensions.FromHex(@"003a4e"); - - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - - Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; - - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(36, 50, 68, 255) - }, - new Triangles - { - TriangleScale = 5, - RelativeSizeAxes = Axes.Both, - ColourLight = new Color4(53, 66, 82, 255), - ColourDark = new Color4(41, 54, 70, 255), - }, - }, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 90), - new Dimension(), - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(10).Opacity(100), - }, - new FillFlowContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Width = content_width, - Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"Gameplay Mods", - Font = OsuFont.GetFont(size: 22, weight: FontWeight.Bold), - Shadow = true, - Margin = new MarginPadding - { - Bottom = 4, - }, - }, - new OsuTextFlowContainer(text => - { - text.Font = text.Font.With(size: 18); - text.Shadow = true; - }) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Text = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.\nOthers are just for fun.", - }, - }, - }, - }, - }, - }, - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - // Body - new OsuScrollContainer - { - ScrollbarVisible = false, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Vertical = 10, - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING - }, - Children = new Drawable[] - { - ModSectionsContainer = new FillFlowContainer - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Width = content_width, - LayoutDuration = 200, - LayoutEasing = Easing.OutQuint, - Children = new[] - { - CreateModSection(ModType.DifficultyReduction).With(s => - { - s.ToggleKeys = new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.DifficultyIncrease).With(s => - { - s.ToggleKeys = new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Automation).With(s => - { - s.ToggleKeys = new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Conversion).With(s => - { - s.Action = modButtonPressed; - }), - CreateModSection(ModType.Fun).With(s => - { - s.Action = modButtonPressed; - }), - } - }, - } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding(30), - Width = 0.3f, - Children = new Drawable[] - { - ModSettingsContainer = new ModSettingsContainer - { - Alpha = 0, - SelectedMods = { BindTarget = SelectedMods }, - }, - } - }, - } - }, - }, - new Drawable[] - { - new Container - { - Name = "Footer content", - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(172, 20, 116, 255), - Alpha = 0.5f, - }, - FooterContainer = new FillFlowContainer - { - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - RelativePositionAxes = Axes.X, - Width = content_width, - Spacing = new Vector2(footer_button_spacing, footer_button_spacing / 2), - Padding = new MarginPadding - { - Vertical = 15, - Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING - }, - Children = new[] - { - DeselectAllButton = new TriangleButton - { - Width = 180, - Text = "Deselect All", - Action = deselectAll, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - CustomiseButton = new TriangleButton - { - Width = 180, - Text = "Customisation", - Action = () => ModSettingsContainer.ToggleVisibility(), - Enabled = { Value = false }, - Alpha = AllowConfiguration ? 1 : 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - CloseButton = new TriangleButton - { - Width = 180, - Text = CommonStrings.ButtonsClose, - Action = Hide, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - } - } - }, - } - }, - }, - }, - }; - - ((IBindable)CustomiseButton.Enabled).BindTo(ModSettingsContainer.HasSettingsForSelection); - } - - [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuGameBase osu) - { - availableMods = osu.AvailableMods.GetBoundCopy(); - - sampleOn = audio.Samples.Get(@"UI/check-on"); - sampleOff = audio.Samples.Get(@"UI/check-off"); - } - - private void deselectAll() - { - foreach (var section in ModSectionsContainer.Children) - section.DeselectAll(); - - refreshSelectedMods(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - availableMods.BindValueChanged(_ => updateAvailableMods(), true); - - // intentionally bound after the above line to avoid a potential update feedback cycle. - // i haven't actually observed this happening but as updateAvailableMods() changes the selection it is plausible. - SelectedMods.BindValueChanged(_ => updateSelectedButtons()); - } - - protected override void PopOut() - { - base.PopOut(); - - foreach (var section in ModSectionsContainer) - { - section.FlushPendingSelections(); - } - - FooterContainer.MoveToX(content_width, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - FooterContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - - foreach (var section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine); - } - } - - protected override void PopIn() - { - base.PopIn(); - - FooterContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - FooterContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - - foreach (var section in ModSectionsContainer.Children) - { - section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); - section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); - } - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - // don't absorb control as ToolbarRulesetSelector uses control + number to navigate - if (e.ControlPressed) return false; - - switch (e.Key) - { - case Key.Number1: - DeselectAllButton.TriggerClick(); - return true; - - case Key.Number2: - CloseButton.TriggerClick(); - return true; - } - - return base.OnKeyDown(e); - } - - public override bool OnPressed(KeyBindingPressEvent e) => false; // handled by back button - - private void updateAvailableMods() - { - if (availableMods?.Value == null) - return; - - foreach (var section in ModSectionsContainer.Children) - { - IEnumerable modEnumeration = availableMods.Value[section.ModType]; - - if (!Stacked) - modEnumeration = ModUtils.FlattenMods(modEnumeration); - - section.Mods = modEnumeration.Select(getValidModOrNull).Where(m => m != null).Select(m => m.DeepClone()); - } - - updateSelectedButtons(); - OnAvailableModsChanged(); - } - - /// - /// Returns a valid form of a given if possible, or null otherwise. - /// - /// - /// This is a recursive process during which any invalid mods are culled while preserving structures where possible. - /// - /// The to check. - /// A valid form of if exists, or null otherwise. - [CanBeNull] - private Mod getValidModOrNull([NotNull] Mod mod) - { - if (!(mod is MultiMod multi)) - return IsValidMod(mod) ? mod : null; - - var validSubset = multi.Mods.Select(getValidModOrNull).Where(m => m != null).ToArray(); - - if (validSubset.Length == 0) - return null; - - return validSubset.Length == 1 ? validSubset[0] : new MultiMod(validSubset); - } - - private void updateSelectedButtons() - { - // Enumeration below may update the bindable list. - var selectedMods = SelectedMods.Value.ToList(); - - foreach (var section in ModSectionsContainer.Children) - section.UpdateSelectedButtons(selectedMods); - } - - private void modButtonPressed(Mod selectedMod) - { - if (selectedMod != null) - { - if (State.Value == Visibility.Visible) - Scheduler.AddOnce(playSelectedSound); - - OnModSelected(selectedMod); - - if (selectedMod.RequiresConfiguration && AllowConfiguration) - ModSettingsContainer.Show(); - } - else - { - if (State.Value == Visibility.Visible) - Scheduler.AddOnce(playDeselectedSound); - } - - refreshSelectedMods(); - } - - private void playSelectedSound() => sampleOn?.Play(); - private void playDeselectedSound() => sampleOff?.Play(); - - /// - /// Invoked after has changed. - /// - protected virtual void OnAvailableModsChanged() - { - } - - /// - /// Invoked when a new has been selected. - /// - /// The that has been selected. - protected virtual void OnModSelected(Mod mod) - { - } - - private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - - /// - /// Creates a that groups s with the same . - /// - /// The of s in the section. - /// The . - protected virtual ModSection CreateModSection(ModType type) => new ModSection(type); - - #region Disposal - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - availableMods?.UnbindAll(); - SelectedMods?.UnbindAll(); - } - - #endregion - } -} From dfd97701d7d17ca559f0d681d8a669cbe2cafb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:53:42 +0200 Subject: [PATCH 47/76] Remove old mod settings container & related controls --- osu.Game/Overlays/Mods/ModControlSection.cs | 54 --------- .../Overlays/Mods/ModSettingsContainer.cs | 111 ------------------ 2 files changed, 165 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModControlSection.cs delete mode 100644 osu.Game/Overlays/Mods/ModSettingsContainer.cs diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs deleted file mode 100644 index 10b3bc7c2b..0000000000 --- a/osu.Game/Overlays/Mods/ModControlSection.cs +++ /dev/null @@ -1,54 +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.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osuTK; - -namespace osu.Game.Overlays.Mods -{ - public class ModControlSection : CompositeDrawable - { - protected FillFlowContainer FlowContent; - - public readonly Mod Mod; - - public ModControlSection(Mod mod, IEnumerable modControls) - { - Mod = mod; - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - FlowContent = new FillFlowContainer - { - Margin = new MarginPadding { Top = 30 }, - Spacing = new Vector2(0, 5), - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - ChildrenEnumerable = modControls - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AddRangeInternal(new Drawable[] - { - new OsuSpriteText - { - Text = Mod.Name, - Font = OsuFont.GetFont(weight: FontWeight.Bold), - Colour = colours.Yellow, - }, - FlowContent - }); - } - } -} diff --git a/osu.Game/Overlays/Mods/ModSettingsContainer.cs b/osu.Game/Overlays/Mods/ModSettingsContainer.cs deleted file mode 100644 index 64d65cab3b..0000000000 --- a/osu.Game/Overlays/Mods/ModSettingsContainer.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Configuration; -using osu.Game.Graphics.Containers; -using osu.Game.Rulesets.Mods; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class ModSettingsContainer : VisibilityContainer - { - public readonly IBindable> SelectedMods = new Bindable>(Array.Empty()); - - public IBindable HasSettingsForSelection => hasSettingsForSelection; - - private readonly Bindable hasSettingsForSelection = new Bindable(); - - private readonly FillFlowContainer modSettingsContent; - - private readonly Container content; - - private const double transition_duration = 400; - - public ModSettingsContainer() - { - RelativeSizeAxes = Axes.Both; - - Child = content = new Container - { - Masking = true, - CornerRadius = 10, - RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Both, - X = 1, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = new Color4(0, 0, 0, 192) - }, - new OsuScrollContainer - { - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = false, - Child = modSettingsContent = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0f, 10f), - Padding = new MarginPadding(20), - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - SelectedMods.BindValueChanged(modsChanged, true); - } - - private void modsChanged(ValueChangedEvent> mods) - { - modSettingsContent.Clear(); - - foreach (var mod in mods.NewValue) - { - var settings = mod.CreateSettingsControls().ToList(); - if (settings.Count > 0) - modSettingsContent.Add(new ModControlSection(mod, settings)); - } - - bool hasSettings = modSettingsContent.Count > 0; - - if (!hasSettings) - Hide(); - - hasSettingsForSelection.Value = hasSettings; - } - - protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnHover(HoverEvent e) => true; - - protected override void PopIn() - { - this.FadeIn(transition_duration, Easing.OutQuint); - content.MoveToX(0, transition_duration, Easing.OutQuint); - } - - protected override void PopOut() - { - this.FadeOut(transition_duration, Easing.OutQuint); - content.MoveToX(1, transition_duration, Easing.OutQuint); - } - } -} From 8b0ece1c09df40506f813a125bbd9c2f78546eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:55:54 +0200 Subject: [PATCH 48/76] Remove old mod section --- osu.Game/Overlays/Mods/ModSection.cs | 261 --------------------------- 1 file changed, 261 deletions(-) delete mode 100644 osu.Game/Overlays/Mods/ModSection.cs diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs deleted file mode 100644 index a70191a864..0000000000 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ /dev/null @@ -1,261 +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 osuTK; -using osuTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; -using Humanizer; -using osu.Framework.Input.Events; -using osu.Game.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class ModSection : CompositeDrawable - { - private readonly Drawable header; - - public FillFlowContainer ButtonsContainer { get; } - - protected IReadOnlyList Buttons { get; private set; } = Array.Empty(); - - public Action Action; - - public Key[] ToggleKeys; - - public readonly ModType ModType; - - public IEnumerable SelectedMods => Buttons.Select(b => b.SelectedMod).Where(m => m != null); - - private CancellationTokenSource modsLoadCts; - - protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0; - - /// - /// True when all mod icons have completed loading. - /// - public bool ModIconsLoaded { get; private set; } = true; - - public IEnumerable Mods - { - set - { - var modContainers = value.Select(m => - { - if (m == null) - return new ModButtonEmpty(); - - return CreateModButton(m).With(b => - { - b.SelectionChanged = mod => - { - ModButtonStateChanged(mod); - Action?.Invoke(mod); - }; - }); - }).ToArray(); - - modsLoadCts?.Cancel(); - - if (modContainers.Length == 0) - { - ModIconsLoaded = true; - header.Hide(); - Hide(); - return; - } - - ModIconsLoaded = false; - - LoadComponentsAsync(modContainers, c => - { - ModIconsLoaded = true; - ButtonsContainer.ChildrenEnumerable = c; - }, (modsLoadCts = new CancellationTokenSource()).Token); - - Buttons = modContainers.OfType().ToArray(); - - header.FadeIn(200); - this.FadeIn(200); - } - } - - protected virtual void ModButtonStateChanged(Mod mod) - { - } - - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.ControlPressed) return false; - - if (ToggleKeys != null) - { - int index = Array.IndexOf(ToggleKeys, e.Key); - if (index > -1 && index < Buttons.Count) - Buttons[index].SelectNext(e.ShiftPressed ? -1 : 1); - } - - return base.OnKeyDown(e); - } - - private const double initial_multiple_selection_delay = 120; - - private double selectionDelay = initial_multiple_selection_delay; - private double lastSelection; - - private readonly Queue pendingSelectionOperations = new Queue(); - - protected override void Update() - { - base.Update(); - - if (selectionDelay == initial_multiple_selection_delay || Time.Current - lastSelection >= selectionDelay) - { - if (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) - { - dequeuedAction(); - - // each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements). - selectionDelay = Math.Max(30, selectionDelay * 0.8f); - lastSelection = Time.Current; - } - else - { - // reset the selection delay after all animations have been completed. - // this will cause the next action to be immediately performed. - selectionDelay = initial_multiple_selection_delay; - } - } - } - - /// - /// Selects all mods. - /// - public void SelectAll() - { - pendingSelectionOperations.Clear(); - - foreach (var button in Buttons.Where(b => !b.Selected)) - pendingSelectionOperations.Enqueue(() => button.SelectAt(0)); - } - - /// - /// Deselects all mods. - /// - public void DeselectAll() - { - pendingSelectionOperations.Clear(); - DeselectTypes(Buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); - } - - /// - /// Deselect one or more mods in this section. - /// - /// The types of s which should be deselected. - /// Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow. - /// If this deselection is triggered by a user selection, this should contain the newly selected type. This type will never be deselected, even if it matches one provided in . - public void DeselectTypes(IEnumerable modTypes, bool immediate = false, Mod newSelection = null) - { - foreach (var button in Buttons) - { - if (button.SelectedMod == null) continue; - - if (button.SelectedMod == newSelection) - continue; - - foreach (var type in modTypes) - { - if (type.IsInstanceOfType(button.SelectedMod)) - { - if (immediate) - button.Deselect(); - else - pendingSelectionOperations.Enqueue(button.Deselect); - } - } - } - } - - /// - /// Updates all buttons with the given list of selected mods. - /// - /// The new list of selected mods to select. - public void UpdateSelectedButtons(IReadOnlyList newSelectedMods) - { - foreach (var button in Buttons) - updateButtonSelection(button, newSelectedMods); - } - - private void updateButtonSelection(ModButton button, IReadOnlyList newSelectedMods) - { - foreach (var mod in newSelectedMods) - { - int index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); - if (index < 0) - continue; - - var buttonMod = button.Mods[index]; - - // as this is likely coming from an external change, ensure the settings of the mod are in sync. - buttonMod.CopyFrom(mod); - - button.SelectAt(index, false); - return; - } - - button.Deselect(); - } - - public ModSection(ModType type) - { - ModType = type; - - AutoSizeAxes = Axes.Y; - RelativeSizeAxes = Axes.X; - - Origin = Anchor.TopCentre; - Anchor = Anchor.TopCentre; - - InternalChildren = new[] - { - header = CreateHeader(type.Humanize(LetterCasing.Title)), - ButtonsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - Spacing = new Vector2(50f, 0f), - Margin = new MarginPadding - { - Top = 20, - }, - AlwaysPresent = true - }, - }; - } - - protected virtual Drawable CreateHeader(string text) => new OsuSpriteText - { - Font = OsuFont.GetFont(weight: FontWeight.Bold), - Text = text - }; - - protected virtual ModButton CreateModButton(Mod mod) => new ModButton(mod); - - /// - /// Run any delayed selections (due to animation) immediately to leave mods in a good (final) state. - /// - public void FlushPendingSelections() - { - while (pendingSelectionOperations.TryDequeue(out var dequeuedAction)) - dequeuedAction(); - } - } -} From 33634cba1eb7badf4821c04a7a21954b55d7f11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 21:58:22 +0200 Subject: [PATCH 49/76] Remove old mod buttons --- .../UserInterface/TestSceneModButton.cs | 64 ---- osu.Game/Overlays/Mods/IncompatibleIcon.cs | 64 ---- osu.Game/Overlays/Mods/ModButton.cs | 319 ------------------ osu.Game/Overlays/Mods/ModButtonEmpty.cs | 20 -- 4 files changed, 467 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs delete mode 100644 osu.Game/Overlays/Mods/IncompatibleIcon.cs delete mode 100644 osu.Game/Overlays/Mods/ModButton.cs delete mode 100644 osu.Game/Overlays/Mods/ModButtonEmpty.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs deleted file mode 100644 index fdc21d80ff..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneModButton : OsuTestScene - { - public TestSceneModButton() - { - Children = new Drawable[] - { - new ModButton(new MultiMod(new TestMod1(), new TestMod2(), new TestMod3(), new TestMod4())) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - } - }; - } - - private class TestMod1 : TestMod - { - public override string Name => "Test mod 1"; - - public override string Acronym => "M1"; - } - - private class TestMod2 : TestMod - { - public override string Name => "Test mod 2"; - - public override string Acronym => "M2"; - - public override IconUsage? Icon => FontAwesome.Solid.Exclamation; - } - - private class TestMod3 : TestMod - { - public override string Name => "Test mod 3"; - - public override string Acronym => "M3"; - - public override IconUsage? Icon => FontAwesome.Solid.ArrowRight; - } - - private class TestMod4 : TestMod - { - public override string Name => "Test mod 4"; - - public override string Acronym => "M4"; - } - - private abstract class TestMod : Mod, IApplicableMod - { - public override double ScoreMultiplier => 1.0; - - public override string Description => "This is a test mod."; - } - } -} diff --git a/osu.Game/Overlays/Mods/IncompatibleIcon.cs b/osu.Game/Overlays/Mods/IncompatibleIcon.cs deleted file mode 100644 index df134fe4a4..0000000000 --- a/osu.Game/Overlays/Mods/IncompatibleIcon.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Overlays.Mods -{ - public class IncompatibleIcon : VisibilityContainer, IHasTooltip - { - private Circle circle; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Size = new Vector2(20); - - State.Value = Visibility.Hidden; - Alpha = 0; - - InternalChildren = new Drawable[] - { - circle = new Circle - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray4, - }, - new SpriteIcon - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(0.6f), - Icon = FontAwesome.Solid.Slash, - Colour = Color4.White, - Shadow = true, - } - }; - } - - protected override void PopIn() - { - this.FadeIn(200, Easing.OutQuint); - circle.FlashColour(Color4.Red, 500, Easing.OutQuint); - this.ScaleTo(1.8f).Then().ScaleTo(1, 500, Easing.OutQuint); - } - - protected override void PopOut() - { - this.FadeOut(200, Easing.OutQuint); - this.ScaleTo(0.8f, 200, Easing.In); - } - - public LocalisableString TooltipText => "Incompatible with current selected mods"; - } -} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs deleted file mode 100644 index 979e2c8da3..0000000000 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ /dev/null @@ -1,319 +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 osuTK; -using osuTK.Graphics; -using osuTK.Input; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using System; -using System.Linq; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.Events; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Mods -{ - /// - /// Represents a clickable button which can cycle through one of more mods. - /// - public class ModButton : ModButtonEmpty, IHasCustomTooltip - { - private ModIcon foregroundIcon; - private ModIcon backgroundIcon; - private readonly SpriteText text; - private readonly Container iconsContainer; - - /// - /// Fired when the selection changes. - /// - public Action SelectionChanged; - - public LocalisableString TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; - - private const Easing mod_switch_easing = Easing.InOutSine; - private const double mod_switch_duration = 120; - - // A selected index of -1 means not selected. - private int selectedIndex = -1; - - /// - /// Change the selected mod index of this button. - /// - /// The new index. - /// Whether any settings applied to the mod should be reset on selection. - /// Whether the selection changed. - private bool changeSelectedIndex(int newIndex, bool resetSettings = true) - { - if (newIndex == selectedIndex) return false; - - int direction = newIndex < selectedIndex ? -1 : 1; - - bool beforeSelected = Selected; - - Mod previousSelection = SelectedMod ?? Mods[0]; - - if (newIndex >= Mods.Length) - newIndex = -1; - else if (newIndex < -1) - newIndex = Mods.Length - 1; - - if (newIndex >= 0 && !Mods[newIndex].HasImplementation) - return false; - - selectedIndex = newIndex; - - Mod newSelection = SelectedMod ?? Mods[0]; - - if (resetSettings) - newSelection.ResetSettingsToDefaults(); - - Schedule(() => - { - if (beforeSelected != Selected) - { - iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic); - iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic); - } - - if (previousSelection != newSelection) - { - const float rotate_angle = 16; - - foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing); - backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing); - - backgroundIcon.Mod = newSelection; - - using (BeginDelayedSequence(mod_switch_duration)) - { - foregroundIcon - .RotateTo(-rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - backgroundIcon - .RotateTo(rotate_angle * direction) - .RotateTo(0f, mod_switch_duration, mod_switch_easing); - - Schedule(() => DisplayMod(newSelection)); - } - } - - foregroundIcon.Selected.Value = Selected; - }); - - SelectionChanged?.Invoke(SelectedMod); - - return true; - } - - public bool Selected => selectedIndex != -1; - - private Color4 selectedColour; - - public Color4 SelectedColour - { - get => selectedColour; - set - { - if (value == selectedColour) return; - - selectedColour = value; - if (Selected) foregroundIcon.Colour = value; - } - } - - private Mod mod; - - protected readonly Container ButtonContent; - - public Mod Mod - { - get => mod; - set - { - mod = value; - - if (mod == null) - { - Mods = Array.Empty(); - Alpha = 0; - } - else - { - Mods = (mod as MultiMod)?.Mods ?? new[] { mod }; - Alpha = 1; - } - - createIcons(); - - if (Mods.Length > 0) - { - DisplayMod(Mods[0]); - } - } - } - - public Mod[] Mods { get; private set; } - - public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); - - protected override bool OnMouseDown(MouseDownEvent e) - { - ButtonContent.ScaleTo(0.9f, 800, Easing.Out); - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - ButtonContent.ScaleTo(1, 500, Easing.OutElastic); - - // only trigger the event if we are inside the area of the button - if (Contains(e.ScreenSpaceMousePosition)) - { - switch (e.Button) - { - case MouseButton.Right: - SelectNext(-1); - break; - } - } - } - - protected override bool OnClick(ClickEvent e) - { - SelectNext(1); - - return true; - } - - /// - /// Select the next available mod in a specified direction. - /// - /// 1 for forwards, -1 for backwards. - public void SelectNext(int direction) - { - int start = selectedIndex + direction; - // wrap around if we are at an extremity. - if (start >= Mods.Length) - start = -1; - else if (start < -1) - start = Mods.Length - 1; - - for (int i = start; i < Mods.Length && i >= 0; i += direction) - { - if (SelectAt(i)) - return; - } - - Deselect(); - } - - /// - /// Select the mod at the provided index. - /// - /// The index to select. - /// Whether any settings applied to the mod should be reset on selection. - /// Whether the selection changed. - public bool SelectAt(int index, bool resetSettings = true) - { - if (!Mods[index].HasImplementation) return false; - - changeSelectedIndex(index, resetSettings); - return true; - } - - public void Deselect() => changeSelectedIndex(-1); - - protected virtual void DisplayMod(Mod mod) - { - if (backgroundIcon != null) - backgroundIcon.Mod = foregroundIcon.Mod; - foregroundIcon.Mod = mod; - text.Text = mod.Name; - Colour = mod.HasImplementation ? Color4.White : Color4.Gray; - } - - private void createIcons() - { - iconsContainer.Clear(); - - if (Mods.Length > 1) - { - iconsContainer.AddRange(new[] - { - backgroundIcon = new ModIcon(Mods[1], false) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(1.5f), - }, - foregroundIcon = new ModIcon(Mods[0], false) - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - Position = new Vector2(-1.5f), - }, - }); - } - else - { - iconsContainer.Add(foregroundIcon = new ModIcon(Mod, false) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }); - } - } - - public ModButton(Mod mod) - { - Children = new Drawable[] - { - new Container - { - Size = new Vector2(77f, 80f), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Children = new Drawable[] - { - ButtonContent = new Container - { - Children = new Drawable[] - { - iconsContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }, - }, - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - } - }, - text = new OsuSpriteText - { - Y = 75, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Font = OsuFont.GetFont(size: 18) - }, - new HoverSounds() - }; - Mod = mod; - } - - public virtual ITooltip GetCustomTooltip() => new ModButtonTooltip(); - - public Mod TooltipContent => SelectedMod ?? Mods.FirstOrDefault(); - } -} diff --git a/osu.Game/Overlays/Mods/ModButtonEmpty.cs b/osu.Game/Overlays/Mods/ModButtonEmpty.cs deleted file mode 100644 index 03afe5adba..0000000000 --- a/osu.Game/Overlays/Mods/ModButtonEmpty.cs +++ /dev/null @@ -1,20 +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 osuTK; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Overlays.Mods -{ - /// - /// A mod button used exclusively for providing an empty space the size of a mod button. - /// - public class ModButtonEmpty : Container - { - public ModButtonEmpty() - { - Size = new Vector2(100f); - AlwaysPresent = true; - } - } -} From c4c7556fb24407b7c97067729a6bd6c894a71eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 22:07:24 +0200 Subject: [PATCH 50/76] Make remains of `TestSceneModSettings` non-visual As they're not really testing anything visual. --- osu.Game.Tests/Mods/ModSettingsTest.cs | 36 ++++++++++++++ .../UserInterface/TestSceneModSettings.cs | 49 ------------------- 2 files changed, 36 insertions(+), 49 deletions(-) create mode 100644 osu.Game.Tests/Mods/ModSettingsTest.cs delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs diff --git a/osu.Game.Tests/Mods/ModSettingsTest.cs b/osu.Game.Tests/Mods/ModSettingsTest.cs new file mode 100644 index 0000000000..b9ea1f2567 --- /dev/null +++ b/osu.Game.Tests/Mods/ModSettingsTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Tests.Mods +{ + public class ModSettingsTest + { + [Test] + public void TestModSettingsUnboundWhenCopied() + { + var original = new OsuModDoubleTime(); + var copy = (OsuModDoubleTime)original.DeepClone(); + + original.SpeedChange.Value = 2; + + Assert.That(original.SpeedChange.Value, Is.EqualTo(2.0)); + Assert.That(copy.SpeedChange.Value, Is.EqualTo(1.5)); + } + + [Test] + public void TestMultiModSettingsUnboundWhenCopied() + { + var original = new MultiMod(new OsuModDoubleTime()); + var copy = (MultiMod)original.DeepClone(); + + ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2; + + Assert.That(((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value, Is.EqualTo(2.0)); + Assert.That(((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value, Is.EqualTo(1.5)); + } + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs deleted file mode 100644 index 1cf3b2651c..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ /dev/null @@ -1,49 +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.Framework.Utils; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneModSettings : OsuManualInputManagerTestScene - { - [Test] - public void TestModSettingsUnboundWhenCopied() - { - OsuModDoubleTime original = null; - OsuModDoubleTime copy = null; - - AddStep("create mods", () => - { - original = new OsuModDoubleTime(); - copy = (OsuModDoubleTime)original.DeepClone(); - }); - - AddStep("change property", () => original.SpeedChange.Value = 2); - - AddAssert("original has new value", () => Precision.AlmostEquals(2.0, original.SpeedChange.Value)); - AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, copy.SpeedChange.Value)); - } - - [Test] - public void TestMultiModSettingsUnboundWhenCopied() - { - MultiMod original = null; - MultiMod copy = null; - - AddStep("create mods", () => - { - original = new MultiMod(new OsuModDoubleTime()); - copy = (MultiMod)original.DeepClone(); - }); - - AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2); - - AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value)); - AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value)); - } - } -} From 76c63f1d0a0a2bda6eb56642c0f417ba8656297c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 May 2022 22:29:57 +0200 Subject: [PATCH 51/76] Rename `ModSelect{Screen -> Overlay}` in place of removed old design --- .../TestSceneFreeModSelectScreen.cs | 18 ++-- .../Multiplayer/TestSceneMultiplayer.cs | 2 +- .../TestSceneMultiplayerMatchSongSelect.cs | 2 +- .../TestSceneMultiplayerMatchSubScreen.cs | 2 +- .../Navigation/TestSceneScreenNavigation.cs | 2 +- .../SongSelect/TestScenePlaySongSelect.cs | 2 +- .../UserInterface/TestSceneModSelectScreen.cs | 96 +++++++++---------- osu.Game/Overlays/Mods/ModColumn.cs | 2 +- ...ModSelectScreen.cs => ModSelectOverlay.cs} | 4 +- ...electScreen.cs => UserModSelectOverlay.cs} | 4 +- ...electScreen.cs => FreeModSelectOverlay.cs} | 4 +- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 4 +- .../OnlinePlay/OnlinePlaySongSelect.cs | 6 +- osu.Game/Screens/Select/SongSelect.cs | 4 +- 14 files changed, 76 insertions(+), 76 deletions(-) rename osu.Game/Overlays/Mods/{ModSelectScreen.cs => ModSelectOverlay.cs} (99%) rename osu.Game/Overlays/Mods/{UserModSelectScreen.cs => UserModSelectOverlay.cs} (92%) rename osu.Game/Screens/OnlinePlay/{FreeModSelectScreen.cs => FreeModSelectOverlay.cs} (94%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs index 4eb14542ba..9a24a1c646 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneFreeModSelectScreen : MultiplayerTestScene { - private FreeModSelectScreen freeModSelectScreen; + private FreeModSelectOverlay freeModSelectOverlay; private readonly Bindable>> availableMods = new Bindable>>(); [BackgroundDependencyLoader] @@ -40,8 +40,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddToggleStep("toggle visibility", visible => { - if (freeModSelectScreen != null) - freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden; + if (freeModSelectOverlay != null) + freeModSelectOverlay.State.Value = visible ? Visibility.Visible : Visibility.Hidden; }); } @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { createFreeModSelect(); - AddStep("select difficulty adjust", () => freeModSelectScreen.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); + AddStep("select difficulty adjust", () => freeModSelectOverlay.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() }); AddWaitStep("wait some", 3); AddAssert("customisation area not expanded", () => this.ChildrenOfType().Single().Height == 0); } @@ -72,18 +72,18 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.MoveMouseTo(this.ChildrenOfType().Last()); InputManager.Click(MouseButton.Left); }); - AddUntilStep("all mods deselected", () => !freeModSelectScreen.SelectedMods.Value.Any()); + AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); } private void createFreeModSelect() { - AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen + AddStep("create free mod select screen", () => Child = freeModSelectOverlay = new FreeModSelectOverlay { State = { Value = Visibility.Visible } }); AddUntilStep("all column content loaded", - () => freeModSelectScreen.ChildrenOfType().Any() - && freeModSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + () => freeModSelectOverlay.ChildrenOfType().Any() + && freeModSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); } private bool assertAllAvailableModsSelected() @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var availableMod in allAvailableMods) { - if (freeModSelectScreen.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) + if (freeModSelectOverlay.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) return false; } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 97cb08e3c1..8e45d99eae 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -627,7 +627,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("invoke on back button", () => multiplayerComponents.OnBackButton()); - AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); + AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs index 061fe5715b..eacd80925d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void assertHasFreeModButton(Type type, bool hasButton = true) { AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay", - () => this.ChildrenOfType() + () => this.ChildrenOfType() .Single() .ChildrenOfType() .Where(panel => !panel.Filtered.Value) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 6173580f0b..ca79fa9cb8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("mod select contents loaded", () => this.ChildrenOfType().Any() && this.ChildrenOfType().All(col => col.IsLoaded && col.ItemsLoaded)); AddUntilStep("mod select contains only double time mod", - () => this.ChildrenOfType() + () => this.ChildrenOfType() .SingleOrDefault()? .ChildrenOfType() .SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 9674ef7ae1..a3e0caedb9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -568,7 +568,7 @@ namespace osu.Game.Tests.Visual.Navigation public class TestPlaySongSelect : PlaySongSelect { - public ModSelectScreen ModSelectOverlay => ModSelect; + public ModSelectOverlay ModSelectOverlay => ModSelect; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 083e24be7b..aad7f6b301 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1008,7 +1008,7 @@ namespace osu.Game.Tests.Visual.SongSelect public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; - public new ModSelectScreen ModSelect => base.ModSelect; + public new ModSelectOverlay ModSelect => base.ModSelect; public new void PresentScore(ScoreInfo score) => base.PresentScore(score); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs index fa7758df59..2e2ce6edd7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Resolved] private RulesetStore rulesetStore { get; set; } - private UserModSelectScreen modSelectScreen; + private UserModSelectOverlay modSelectOverlay; [SetUpSteps] public void SetUpSteps() @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void createScreen() { - AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen + AddStep("create screen", () => Child = modSelectOverlay = new UserModSelectOverlay { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestStateChange() { createScreen(); - AddStep("toggle state", () => modSelectScreen.ToggleVisibility()); + AddStep("toggle state", () => modSelectOverlay.ToggleVisibility()); } [Test] @@ -62,14 +62,14 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); createScreen(); - AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); - AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); + AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); } [Test] @@ -77,14 +77,14 @@ namespace osu.Game.Tests.Visual.UserInterface { createScreen(); AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModAlternate(), new OsuModDaycore() }); - AddUntilStep("two panels active", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectScreen.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); - AddAssert("setting items created", () => modSelectScreen.ChildrenOfType().Any()); + AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); } [Test] @@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("last column dimmed", () => !this.ChildrenOfType().Last().Active.Value); AddStep("request scroll to last column", () => { - var lastDimContainer = this.ChildrenOfType().Last(); + var lastDimContainer = this.ChildrenOfType().Last(); lastColumn = lastDimContainer.Column; lastDimContainer.RequestScroll?.Invoke(lastDimContainer); }); @@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("dismiss mod customisation via toggle", () => { - InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().Single()); + InputManager.MoveMouseTo(modSelectOverlay.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); assertCustomisationToggleState(disabled: false, active: false); @@ -205,14 +205,14 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("move mouse to dimmed area", () => { InputManager.MoveMouseTo(new Vector2( - modSelectScreen.ScreenSpaceDrawQuad.TopLeft.X, - (modSelectScreen.ScreenSpaceDrawQuad.TopLeft.Y + modSelectScreen.ScreenSpaceDrawQuad.BottomLeft.Y) / 2)); + modSelectOverlay.ScreenSpaceDrawQuad.TopLeft.X, + (modSelectOverlay.ScreenSpaceDrawQuad.TopLeft.Y + modSelectOverlay.ScreenSpaceDrawQuad.BottomLeft.Y) / 2)); }); AddStep("click", () => InputManager.Click(MouseButton.Left)); assertCustomisationToggleState(disabled: false, active: false); - AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectScreen.ChildrenOfType().First())); - AddAssert("first mod panel is hovered", () => modSelectScreen.ChildrenOfType().First().IsHovered); + AddStep("move mouse to first mod panel", () => InputManager.MoveMouseTo(modSelectOverlay.ChildrenOfType().First())); + AddAssert("first mod panel is hovered", () => modSelectOverlay.ChildrenOfType().First().IsHovered); } /// @@ -222,12 +222,12 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestSettingsNotCrossPolluting() { Bindable> selectedMods2 = null; - ModSelectScreen modSelectScreen2 = null; + ModSelectOverlay modSelectScreen2 = null; createScreen(); AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); - AddStep("set setting", () => modSelectScreen.ChildrenOfType>().First().Current.Value = 8); + AddStep("set setting", () => modSelectOverlay.ChildrenOfType>().First().Current.Value = 8); AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create second overlay", () => { - Add(modSelectScreen2 = new UserModSelectScreen().With(d => + Add(modSelectScreen2 = new UserModSelectOverlay().With(d => { d.Origin = Anchor.TopCentre; d.Anchor = Anchor.TopCentre; @@ -276,20 +276,20 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Select all fun mods", () => { - modSelectScreen.ChildrenOfType() - .Single(c => c.ModType == ModType.DifficultyIncrease) - .SelectAll(); + modSelectOverlay.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .SelectAll(); }); AddUntilStep("many mods selected", () => SelectedMods.Value.Count >= 5); AddStep("trigger deselect and close overlay", () => { - modSelectScreen.ChildrenOfType() - .Single(c => c.ModType == ModType.DifficultyIncrease) - .DeselectAll(); + modSelectOverlay.ChildrenOfType() + .Single(c => c.ModType == ModType.DifficultyIncrease) + .DeselectAll(); - modSelectScreen.Hide(); + modSelectOverlay.Hide(); }); AddAssert("all mods deselected", () => SelectedMods.Value.Count == 0); @@ -378,15 +378,15 @@ namespace osu.Game.Tests.Visual.UserInterface createScreen(); changeRuleset(0); - AddAssert("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + AddAssert("double time visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); - AddStep("make double time invalid", () => modSelectScreen.IsValidMod = m => !(m is OsuModDoubleTime)); - AddUntilStep("double time not visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).All(panel => panel.Filtered.Value)); - AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + AddStep("make double time invalid", () => modSelectOverlay.IsValidMod = m => !(m is OsuModDoubleTime)); + AddUntilStep("double time not visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).All(panel => panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); - AddStep("make double time valid again", () => modSelectScreen.IsValidMod = m => true); - AddUntilStep("double time visible", () => modSelectScreen.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); - AddAssert("nightcore still visible", () => modSelectScreen.ChildrenOfType().Where(b => b.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); + AddStep("make double time valid again", () => modSelectOverlay.IsValidMod = m => true); + AddUntilStep("double time visible", () => modSelectOverlay.ChildrenOfType().Where(panel => panel.Mod is OsuModDoubleTime).Any(panel => !panel.Filtered.Value)); + AddAssert("nightcore still visible", () => modSelectOverlay.ChildrenOfType().Where(b => b.Mod is OsuModNightcore).Any(panel => !panel.Filtered.Value)); } [Test] @@ -396,10 +396,10 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(0); AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); - AddStep("make NF invalid", () => modSelectScreen.IsValidMod = m => !(m is ModNoFail)); - AddAssert("DT + HD still selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddStep("make NF invalid", () => modSelectOverlay.IsValidMod = m => !(m is ModNoFail)); + AddAssert("DT + HD still selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); } [Test] @@ -422,7 +422,7 @@ namespace osu.Game.Tests.Visual.UserInterface changeRuleset(0); AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() }); - AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType().Count(panel => panel.Active.Value) == 2); + AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value) == 2); AddStep("click deselect all button", () => { @@ -448,13 +448,13 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(this.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); - AddAssert("mod select hidden", () => modSelectScreen.State.Value == Visibility.Hidden); + AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden); } [Test] public void TestColumnHiding() { - AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen + AddStep("create screen", () => Child = modSelectOverlay = new UserModSelectOverlay { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, @@ -466,23 +466,23 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("two columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 2); - AddStep("unset filter", () => modSelectScreen.IsValidMod = _ => true); + AddStep("unset filter", () => modSelectOverlay.IsValidMod = _ => true); AddAssert("all columns visible", () => this.ChildrenOfType().All(col => col.IsPresent)); - AddStep("filter out everything", () => modSelectScreen.IsValidMod = _ => false); + AddStep("filter out everything", () => modSelectOverlay.IsValidMod = _ => false); AddAssert("no columns visible", () => this.ChildrenOfType().All(col => !col.IsPresent)); - AddStep("hide", () => modSelectScreen.Hide()); - AddStep("set filter for 3 columns", () => modSelectScreen.IsValidMod = mod => mod.Type == ModType.DifficultyReduction - || mod.Type == ModType.Automation - || mod.Type == ModType.Conversion); + AddStep("hide", () => modSelectOverlay.Hide()); + AddStep("set filter for 3 columns", () => modSelectOverlay.IsValidMod = mod => mod.Type == ModType.DifficultyReduction + || mod.Type == ModType.Automation + || mod.Type == ModType.Conversion); - AddStep("show", () => modSelectScreen.Show()); + AddStep("show", () => modSelectOverlay.Show()); AddUntilStep("3 columns visible", () => this.ChildrenOfType().Count(col => col.IsPresent) == 3); } private void waitForColumnLoad() => AddUntilStep("all column content loaded", - () => modSelectScreen.ChildrenOfType().Any() && modSelectScreen.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); + () => modSelectOverlay.ChildrenOfType().Any() && modSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); private void changeRuleset(int id) { @@ -492,14 +492,14 @@ namespace osu.Game.Tests.Visual.UserInterface private void assertCustomisationToggleState(bool disabled, bool active) { - ShearedToggleButton getToggle() => modSelectScreen.ChildrenOfType().Single(); + ShearedToggleButton getToggle() => modSelectOverlay.ChildrenOfType().Single(); AddAssert($"customisation toggle is {(disabled ? "" : "not ")}disabled", () => getToggle().Active.Disabled == disabled); AddAssert($"customisation toggle is {(active ? "" : "not ")}active", () => getToggle().Active.Value == active); } private ModPanel getPanelForMod(Type modType) - => modSelectScreen.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); + => modSelectOverlay.ChildrenOfType().Single(panel => panel.Mod.GetType() == modType); private class TestUnimplementedMod : Mod { diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 3a2fda0bb0..b32ebb4a5c 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -384,7 +384,7 @@ namespace osu.Game.Overlays.Mods /// /// /// This method exists to be able to receive mod instances that come from potentially-external sources and to copy the changes across to this column's state. - /// uses this to substitute any external mod references in + /// uses this to substitute any external mod references in /// to references that are owned by this column. /// internal void SetSelection(IReadOnlyList mods) diff --git a/osu.Game/Overlays/Mods/ModSelectScreen.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs similarity index 99% rename from osu.Game/Overlays/Mods/ModSelectScreen.cs rename to osu.Game/Overlays/Mods/ModSelectOverlay.cs index 912c09f05c..b589b2e7e1 100644 --- a/osu.Game/Overlays/Mods/ModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,7 +27,7 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public abstract class ModSelectScreen : ShearedOverlayContainer, ISamplePlaybackDisabler + public abstract class ModSelectOverlay : ShearedOverlayContainer, ISamplePlaybackDisabler { protected const int BUTTON_WIDTH = 200; @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Mods private ShearedToggleButton? customisationButton; - protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { } diff --git a/osu.Game/Overlays/Mods/UserModSelectScreen.cs b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs similarity index 92% rename from osu.Game/Overlays/Mods/UserModSelectScreen.cs rename to osu.Game/Overlays/Mods/UserModSelectOverlay.cs index a018797cba..8ff5e28c8f 100644 --- a/osu.Game/Overlays/Mods/UserModSelectScreen.cs +++ b/osu.Game/Overlays/Mods/UserModSelectOverlay.cs @@ -10,9 +10,9 @@ using osuTK.Input; namespace osu.Game.Overlays.Mods { - public class UserModSelectScreen : ModSelectScreen + public class UserModSelectOverlay : ModSelectOverlay { - public UserModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green) + public UserModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs similarity index 94% rename from osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs rename to osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index e92de5e083..6e1c9b7a59 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectScreen.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -13,7 +13,7 @@ using osu.Game.Localisation; namespace osu.Game.Screens.OnlinePlay { - public class FreeModSelectScreen : ModSelectScreen + public class FreeModSelectOverlay : ModSelectOverlay { protected override bool ShowTotalMultiplier => false; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } - public FreeModSelectScreen() + public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) { IsValidMod = _ => true; diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index ec4e329f7a..13b2c37ded 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Match public readonly Room Room; private readonly bool allowEdit; - private ModSelectScreen userModsSelectOverlay; + private ModSelectOverlay userModsSelectOverlay; [CanBeNull] private IDisposable userModsSelectOverlayRegistration; @@ -231,7 +231,7 @@ namespace osu.Game.Screens.OnlinePlay.Match } }; - LoadComponent(userModsSelectOverlay = new UserModSelectScreen(OverlayColourScheme.Plum) + LoadComponent(userModsSelectOverlay = new UserModSelectOverlay(OverlayColourScheme.Plum) { SelectedMods = { BindTarget = UserMods }, IsValidMod = _ => false diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index c4503773ad..fb18a33d66 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.OnlinePlay private IReadOnlyList initialMods; private bool itemSelected; - private readonly FreeModSelectScreen freeModSelectOverlay; + private readonly FreeModSelectOverlay freeModSelectOverlay; private IDisposable freeModSelectOverlayRegistration; protected OnlinePlaySongSelect(Room room) @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; - freeModSelectOverlay = new FreeModSelectScreen + freeModSelectOverlay = new FreeModSelectOverlay { SelectedMods = { BindTarget = FreeMods }, IsValidMod = IsValidFreeMod, @@ -160,7 +160,7 @@ namespace osu.Game.Screens.OnlinePlay return base.OnExiting(e); } - protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum) + protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(OverlayColourScheme.Plum) { IsValidMod = IsValidMod }; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index eb5e996972..8870239485 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Select [Resolved(CanBeNull = true)] private LegacyImportManager legacyImportManager { get; set; } - protected ModSelectScreen ModSelect { get; private set; } + protected ModSelectOverlay ModSelect { get; private set; } protected Sample SampleConfirm { get; private set; } @@ -333,7 +333,7 @@ namespace osu.Game.Screens.Select (new FooterButtonOptions(), BeatmapOptions) }; - protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(); + protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(); protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) { From 9dce329e998e88c415c7d23576d75a29be3ca47f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 10:38:35 +0900 Subject: [PATCH 52/76] Remove build suffix from version when reporting to sentry --- osu.Game/OsuGameBase.cs | 4 +++- osu.Game/Utils/SentryLogger.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ce798d4027..2e4758a134 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -86,6 +86,8 @@ namespace osu.Game public bool IsDeployedBuild => AssemblyVersion.Major > 0; + internal const string BUILD_SUFFIX = "lazer"; + public virtual string Version { get @@ -94,7 +96,7 @@ namespace osu.Game return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release"); var version = AssemblyVersion; - return $@"{version.Major}.{version.Minor}.{version.Build}-lazer"; + return $@"{version.Major}.{version.Minor}.{version.Build}-{BUILD_SUFFIX}"; } } diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index ad4bcf6274..218d10345f 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -34,7 +34,7 @@ namespace osu.Game.Utils options.AutoSessionTracking = true; options.IsEnvironmentUser = false; - options.Release = game.Version; + options.Release = game.Version.Replace($@"-{OsuGameBase.BUILD_SUFFIX}", string.Empty); }); Logger.NewEntry += processLogEntry; From 533f4b298f5f0a34fa690e33c11ac858cae783ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 10:40:40 +0900 Subject: [PATCH 53/76] Add explanation inline for future visitors --- osu.Game/Utils/SentryLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index 218d10345f..16f1a8b039 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -34,6 +34,8 @@ namespace osu.Game.Utils options.AutoSessionTracking = true; options.IsEnvironmentUser = false; + // The reported release needs to match release tags on github in order for sentry + // to automatically associate and track against releases. options.Release = game.Version.Replace($@"-{OsuGameBase.BUILD_SUFFIX}", string.Empty); }); From 843e13a471a0b763ca5cd2f6047478c1fb0241de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 12:55:15 +0900 Subject: [PATCH 54/76] Add screen stack context to sentry --- osu.Game/OsuGame.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b8abef38a8..9edcf90132 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -56,6 +56,8 @@ using osu.Game.Updater; using osu.Game.Users; using osu.Game.Utils; using osuTK.Graphics; +using Sentry; +using Logger = osu.Framework.Logging.Logger; namespace osu.Game { @@ -1197,6 +1199,15 @@ namespace osu.Game private void screenChanged(IScreen current, IScreen newScreen) { + SentrySdk.ConfigureScope(scope => + { + scope.Contexts[@"screen stack"] = new + { + Current = newScreen.GetType().Name, + Previous = current.GetType().Name, + }; + }); + switch (newScreen) { case IntroScreen intro: From b136677bb002565e7736f7947a44fd456685d5df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:03:16 +0900 Subject: [PATCH 55/76] Add config and clock time contexts to sentry --- osu.Game/Configuration/OsuConfigManager.cs | 16 ++++++++++++++++ osu.Game/Utils/SentryLogger.cs | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 64e77384a2..026a83cceb 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; @@ -164,6 +166,20 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.EditorHitAnimations, false); } + public IDictionary GetLoggableState() => + new Dictionary(ConfigStore.Where(kvp => !keyContainsPrivateInformation(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString())); + + private static bool keyContainsPrivateInformation(OsuSetting argKey) + { + switch (argKey) + { + case OsuSetting.Token: + return true; + } + + return false; + } + public OsuConfigManager(Storage storage) : base(storage) { diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index ad4bcf6274..e019e4bb12 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -7,9 +7,12 @@ using System; using System.Diagnostics; using System.IO; using System.Net; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using Sentry; using Sentry.Protocol; @@ -24,8 +27,11 @@ namespace osu.Game.Utils private readonly IDisposable? sentrySession; + private readonly OsuGame game; + public SentryLogger(OsuGame game) { + this.game = game; sentrySession = SentrySdk.Init(options => { // Not setting the dsn will completely disable sentry. @@ -92,6 +98,18 @@ namespace osu.Game.Utils { Message = entry.Message, Level = getSentryLevel(entry.Level), + }, scope => + { + scope.Contexts[@"config"] = new + { + Game = game.Dependencies.Get().GetLoggableState() + // TODO: add framework config here. needs some consideration on how to expose. + }; + scope.Contexts[@"clocks"] = new + { + Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, + Game = game.Clock.CurrentTime, + }; }); } else From 977a0453cc3e18bbc38ba75d77288b0af72834c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:11:20 +0900 Subject: [PATCH 56/76] Add beatmap context to sentry --- osu.Game/Utils/SentryLogger.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index e019e4bb12..d02ffcb013 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -10,6 +10,7 @@ using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -105,6 +106,15 @@ namespace osu.Game.Utils Game = game.Dependencies.Get().GetLoggableState() // TODO: add framework config here. needs some consideration on how to expose. }; + + var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + + scope.Contexts[@"beatmap"] = new + { + Name = beatmap.ToString(), + beatmap.OnlineID, + }; + scope.Contexts[@"clocks"] = new { Audio = game.Dependencies.Get().CurrentTrack.CurrentTime, From f53d42d31ff51e6c43878e49f2db6b3da73a5841 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:51:56 +0900 Subject: [PATCH 57/76] Add realm context to sentry --- osu.Game/Utils/SentryLogger.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index d02ffcb013..a291cee9ae 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -6,14 +6,18 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Skinning; using Sentry; using Sentry.Protocol; @@ -101,13 +105,27 @@ namespace osu.Game.Utils Level = getSentryLevel(entry.Level), }, scope => { + var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + scope.Contexts[@"config"] = new { Game = game.Dependencies.Get().GetLoggableState() // TODO: add framework config here. needs some consideration on how to expose. }; - var beatmap = game.Dependencies.Get>().Value.BeatmapInfo; + game.Dependencies.Get().Run(realm => + { + scope.Contexts[@"realm"] = new + { + Counts = new + { + BeatmapSets = realm.All().Count(), + Beatmaps = realm.All().Count(), + Files = realm.All().Count(), + Skins = realm.All().Count(), + } + }; + }); scope.Contexts[@"beatmap"] = new { From be09ec833e0b5e271f6fe8d50dd3d0e4d965de30 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 14:52:08 +0900 Subject: [PATCH 58/76] Add global statistics context to sentry --- osu.Game/Utils/SentryLogger.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Utils/SentryLogger.cs b/osu.Game/Utils/SentryLogger.cs index a291cee9ae..d68f43e88a 100644 --- a/osu.Game/Utils/SentryLogger.cs +++ b/osu.Game/Utils/SentryLogger.cs @@ -11,6 +11,7 @@ using System.Net; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Logging; +using osu.Framework.Statistics; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; @@ -127,6 +128,10 @@ namespace osu.Game.Utils }; }); + scope.Contexts[@"global statistics"] = GlobalStatistics.GetStatistics() + .GroupBy(s => s.Group) + .ToDictionary(g => g.Key, items => items.ToDictionary(i => i.Name, g => g.DisplayValue)); + scope.Contexts[@"beatmap"] = new { Name = beatmap.ToString(), From 0b597e712ed72de537fc9e8e413bc06a7c477111 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:39:27 +0300 Subject: [PATCH 59/76] Fix timeline not handling mouse down events --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 51b8792d87..7e66c57917 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -270,12 +270,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool OnMouseDown(MouseDownEvent e) { if (base.OnMouseDown(e)) - { beginUserDrag(); - return true; - } - return false; + return true; } protected override void OnMouseUp(MouseUpEvent e) From e43ce28ada3a21a1b7c978da2db16ea9fee0f994 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:51:00 +0300 Subject: [PATCH 60/76] Remove references of "difficulty point" in sample point test scene Fat-fingered. --- .../Editing/TestSceneHitObjectSamplePointAdjustments.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index dca30a6fc0..f0a2347c31 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -173,11 +173,11 @@ namespace osu.Game.Tests.Visual.Editing samplePopoverHasSingleBank("normal"); } - private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} difficulty piece", () => + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => { - var difficultyPiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); + var samplePiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); - InputManager.MoveMouseTo(difficultyPiece); + InputManager.MoveMouseTo(samplePiece); InputManager.Click(MouseButton.Left); }); @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); - AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); + AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } private void setVolumeViaPopover(int volume) => AddStep($"set volume {volume} via popover", () => From f797514bce5aafb5df020e7fa62c2dd24b0e3139 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:51:59 +0300 Subject: [PATCH 61/76] Add failing test cases --- ...tSceneHitObjectDifficultyPointAdjustments.cs | 17 +++++++++++++++++ .../TestSceneHitObjectSamplePointAdjustments.cs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 4012a672ed..7f82d5966e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; @@ -66,6 +67,13 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestPopoverHasFocus() + { + clickDifficultyPiece(0); + velocityPopoverHasFocus(); + } + [Test] public void TestSingleSelection() { @@ -133,6 +141,15 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); + private void velocityPopoverHasFocus() => AddUntilStep("velocity popover textbox focused", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + var textbox = slider?.ChildrenOfType().Single(); + + return textbox?.HasFocus == true; + }); + private void velocityPopoverHasSingleValue(double velocity) => AddUntilStep($"velocity popover has {velocity}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index f0a2347c31..fcdf4a6e33 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -57,6 +57,13 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestPopoverHasFocus() + { + clickSamplePiece(0); + samplePopoverHasFocus(); + } + [Test] public void TestSingleSelection() { @@ -181,6 +188,15 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Click(MouseButton.Left); }); + private void samplePopoverHasFocus() => AddUntilStep("sample popover textbox focused", () => + { + var popover = this.ChildrenOfType().SingleOrDefault(); + var slider = popover?.ChildrenOfType>().Single(); + var textbox = slider?.ChildrenOfType().Single(); + + return textbox?.HasFocus == true; + }); + private void samplePopoverHasSingleVolume(int volume) => AddUntilStep($"sample popover has volume {volume}", () => { var popover = this.ChildrenOfType().SingleOrDefault(); From 24432dffc4622135e1a296638d1e765ca9801477 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:53:04 +0300 Subject: [PATCH 62/76] Add support for focusing intermediate slider-textbox component --- .../Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index 0cf2cf6c54..16a04982f5 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; @@ -107,6 +108,14 @@ namespace osu.Game.Screens.Edit.Timing Current.BindValueChanged(_ => updateState(), true); } + public override bool AcceptsFocus => true; + + protected override void OnFocus(FocusEvent e) + { + base.OnFocus(e); + GetContainingInputManager().ChangeFocus(textBox); + } + private void updateState() { if (Current.Value is T nonNullValue) From 96db530de63626afa8ed51f3f8f24de669a7faa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 09:55:34 +0300 Subject: [PATCH 63/76] Focus textbox on control point popovers --- .../Compose/Components/Timeline/DifficultyPointPiece.cs | 6 ++++++ .../Edit/Compose/Components/Timeline/SamplePointPiece.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index b230bab0c2..eaaa663fe7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -122,6 +122,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline beatmap.EndChange(); }); } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(sliderVelocitySlider)); + } } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index fc0952d4f0..ab21a83c43 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -126,6 +126,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline volume.Current.BindValueChanged(val => updateVolumeFor(relevantObjects, val.NewValue)); } + protected override void LoadComplete() + { + base.LoadComplete(); + ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(volume)); + } + private static string? getCommonBank(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleBank).Distinct().Count() == 1 ? relevantControlPoints.First().SampleBank : null; private static int? getCommonVolume(SampleControlPoint[] relevantControlPoints) => relevantControlPoints.Select(point => point.SampleVolume).Distinct().Count() == 1 ? (int?)relevantControlPoints.First().SampleVolume : null; From 1c369956066c28ffe2333d03ae9353e1cf9a09f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 16:09:16 +0900 Subject: [PATCH 64/76] Expose `HoldingForHUD` state from `HUDOverlay` as bindable --- osu.Game/Screens/Play/HUDOverlay.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index abfed1acd0..f6087e0958 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -68,7 +68,9 @@ namespace osu.Game.Screens.Play internal readonly IBindable IsPlaying = new Bindable(); - private bool holdingForHUD; + public IBindable HoldingForHUD => holdingForHUD; + + private readonly BindableBool holdingForHUD = new BindableBool(); private readonly SkinnableTargetContainer mainComponents; @@ -144,7 +146,8 @@ namespace osu.Game.Screens.Play hideTargets.ForEach(d => d.Hide()); } - public override void Hide() => throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead."); + public override void Hide() => + throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead."); protected override void LoadComplete() { @@ -152,6 +155,7 @@ namespace osu.Game.Screens.Play ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, FADE_DURATION, FADE_EASING))); + holdingForHUD.BindValueChanged(_ => updateVisibility()); IsPlaying.BindValueChanged(_ => updateVisibility()); configVisibilityMode.BindValueChanged(_ => updateVisibility(), true); @@ -204,7 +208,7 @@ namespace osu.Game.Screens.Play if (ShowHud.Disabled) return; - if (holdingForHUD) + if (holdingForHUD.Value) { ShowHud.Value = true; return; @@ -287,8 +291,7 @@ namespace osu.Game.Screens.Play switch (e.Action) { case GlobalAction.HoldForHUD: - holdingForHUD = true; - updateVisibility(); + holdingForHUD.Value = true; return true; case GlobalAction.ToggleInGameInterface: @@ -318,8 +321,7 @@ namespace osu.Game.Screens.Play switch (e.Action) { case GlobalAction.HoldForHUD: - holdingForHUD = false; - updateVisibility(); + holdingForHUD.Value = false; break; } } From d05cd6908763cd47ca66281f70d6540b2080ea39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 16:12:54 +0900 Subject: [PATCH 65/76] Change multiplayer leaderboard to always hide during gameplay unless holding-for-HUD --- .../OnlinePlay/Multiplayer/MultiplayerPlayer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index 02ff040a94..5dab845999 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -43,6 +43,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private readonly MultiplayerRoomUser[] users; + private readonly Bindable leaderboardExpanded = new BindableBool(); + private LoadingLayer loadingDisplay; private FillFlowContainer leaderboardFlow; @@ -76,13 +78,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer Spacing = new Vector2(5) }); + HUDOverlay.HoldingForHUD.BindValueChanged(_ => updateLeaderboardExpandedState()); + LocalUserPlaying.BindValueChanged(_ => updateLeaderboardExpandedState(), true); + // todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area. LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(GameplayState.Ruleset.RulesetInfo, ScoreProcessor, users), l => { if (!LoadedBeatmapSuccessfully) return; - ((IBindable)leaderboard.Expanded).BindTo(HUDOverlay.ShowHud); + leaderboard.Expanded.BindTo(leaderboardExpanded); leaderboardFlow.Insert(0, l); @@ -99,7 +104,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer LoadComponentAsync(new GameplayChatDisplay(Room) { - Expanded = { BindTarget = HUDOverlay.ShowHud }, + Expanded = { BindTarget = leaderboardExpanded }, }, chat => leaderboardFlow.Insert(2, chat)); HUDOverlay.Add(loadingDisplay = new LoadingLayer(true) { Depth = float.MaxValue }); @@ -152,6 +157,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } } + private void updateLeaderboardExpandedState() => + leaderboardExpanded.Value = !LocalUserPlaying.Value || HUDOverlay.HoldingForHUD.Value; + private void failAndBail(string message = null) { if (!string.IsNullOrEmpty(message)) From d51689e9ae38a0f0030ac436701d22d57465b51d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 11:25:41 +0300 Subject: [PATCH 66/76] Unfocus textbox when dismissing popover in test scene --- .../Editing/TestSceneHitObjectDifficultyPointAdjustments.cs | 1 + .../Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 7f82d5966e..7c05abc2cd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -168,6 +168,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { + AddStep("unfocus textbox", () => InputManager.Key(Key.Escape)); AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index fcdf4a6e33..4501eea88e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -231,6 +231,7 @@ namespace osu.Game.Tests.Visual.Editing private void dismissPopover() { + AddStep("unfocus textbox", () => InputManager.Key(Key.Escape)); AddStep("dismiss popover", () => InputManager.Key(Key.Escape)); AddUntilStep("wait for dismiss", () => !this.ChildrenOfType().Any(popover => popover.IsPresent)); } From 551370d27b1a9c4e941335e706efc9bc19896cfe Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 11 May 2022 18:26:08 +0900 Subject: [PATCH 67/76] Update ReSharper package --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1132396608..65ac05261a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.1.0-eap10", + "version": "2022.1.1", "commands": [ "jb" ] @@ -27,4 +27,4 @@ ] } } -} +} \ No newline at end of file From a46894b613df38cb0d4d159f6e86a87dd2a0e8fb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 11 May 2022 18:27:57 +0900 Subject: [PATCH 68/76] Bust CI cache on more files --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 514acef525..729f2f266d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ github.workspace }}/inspectcode - key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }} + key: inspectcode-${{ hashFiles('.config/dotnet-tools.json', '.github/workflows/ci.yml', 'osu.sln*', '.editorconfig', '.globalconfig') }} - name: Dotnet code style run: dotnet build -c Debug -warnaserror osu.Desktop.slnf -p:EnforceCodeStyleInBuild=true From fecf92e16f926b2d5e2a8f51b2e3c03bf8736d0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 19:51:22 +0900 Subject: [PATCH 69/76] Update libraries to latest versions --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- osu.Android.props | 2 +- osu.Desktop/osu.Desktop.csproj | 2 +- osu.Game.Benchmarks/osu.Game.Benchmarks.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- .../osu.Game.Tournament.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 14 +++++++------- osu.iOS.props | 8 ++++---- 15 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index cb922c5a58..bc285dbe11 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 5ecd9cc675..718ada1905 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index 33ad0ac4f7..6b9c3f4d63 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 5ecd9cc675..718ada1905 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/osu.Android.props b/osu.Android.props index 97d9dbc380..af4a93d191 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -56,6 +56,6 @@ - + diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index a4f309c6ac..a4f9e2671b 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index 434c0e0367..36ffd3b5b6 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index fc6d900567..b957ade952 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index ddad2adfea..d3b4b378c0 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 4ce29ab5c7..2c0d3fd937 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index a6b8eb8651..ce468d399b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0bcf533653..a1eef4ce47 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index c7314a4969..6fd53d923b 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2f32c843c0..89cb882981 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,9 +23,9 @@ - - - + + + @@ -34,12 +34,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - + + + diff --git a/osu.iOS.props b/osu.iOS.props index b483267696..46580705f7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -85,10 +85,10 @@ - - + + - - + + From 3cbd19a9ce09c800005e6b51d263e4d4593362d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 May 2022 21:17:16 +0900 Subject: [PATCH 70/76] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index af4a93d191..98dc28d915 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 89cb882981..772a78c8fe 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 46580705f7..af8f9b617c 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 9d30b490ade53ada8771a704a03163c04f15028f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 11 May 2022 18:26:04 +0300 Subject: [PATCH 71/76] Fix intermittent test failures in results screen --- .../Visual/Ranking/TestSceneResultsScreen.cs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index a1d51683e4..2a5fc050d3 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Ranking score.Accuracy = accuracy; score.Rank = rank; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen(score))); + loadResultsScreen(() => screen = createResultsScreen(score)); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Ranking { UnrankedSoloResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createUnrankedSoloResultsScreen())); + loadResultsScreen(() => screen = createUnrankedSoloResultsScreen()); AddUntilStep("wait for loaded", () => screen.IsLoaded); AddAssert("retry overlay present", () => screen.RetryOverlay != null); } @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddStep("click expanded panel", () => @@ -201,7 +201,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); ScorePanel expandedPanel = null; @@ -231,7 +231,7 @@ namespace osu.Game.Tests.Visual.Ranking var tcs = new TaskCompletionSource(); - AddStep("load results", () => Child = new TestResultsContainer(screen = new DelayedFetchResultsScreen(TestResources.CreateTestScoreInfo(), tcs.Task))); + loadResultsScreen(() => screen = new DelayedFetchResultsScreen(TestResources.CreateTestScoreInfo(), tcs.Task)); AddUntilStep("wait for loaded", () => screen.IsLoaded); @@ -255,7 +255,7 @@ namespace osu.Game.Tests.Visual.Ranking { TestResultsScreen screen = null; - AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen())); + loadResultsScreen(() => screen = createResultsScreen()); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddAssert("download button is disabled", () => !screen.ChildrenOfType().Last().Enabled.Value); @@ -276,7 +276,7 @@ namespace osu.Game.Tests.Visual.Ranking var ruleset = new RulesetWithNoPerformanceCalculator(); var score = TestResources.CreateTestScoreInfo(ruleset.RulesetInfo); - AddStep("load results", () => Child = new TestResultsContainer(createResultsScreen(score))); + loadResultsScreen(() => createResultsScreen(score)); AddUntilStep("wait for load", () => this.ChildrenOfType().Single().AllPanelsVisible); AddAssert("PP displayed as 0", () => @@ -287,6 +287,22 @@ namespace osu.Game.Tests.Visual.Ranking }); } + private void loadResultsScreen(Func createResults) + { + ResultsScreen results = null; + + AddStep("load results", () => Child = new TestResultsContainer(results = createResults())); + + // expanded panel should be centered the moment results screen is loaded + // but can potentially be scrolled away on certain specific load scenarios. + // see: https://github.com/ppy/osu/issues/18226 + AddUntilStep("expanded panel in centre of screen", () => + { + var expandedPanel = this.ChildrenOfType().Single(p => p.State == PanelState.Expanded); + return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, results.ScreenSpaceDrawQuad.Centre.X, 1); + }); + } + private TestResultsScreen createResultsScreen(ScoreInfo score = null) => new TestResultsScreen(score ?? TestResources.CreateTestScoreInfo()); private UnrankedSoloResultsScreen createUnrankedSoloResultsScreen() => new UnrankedSoloResultsScreen(TestResources.CreateTestScoreInfo()); From a104277e7fbd1315fa230fa67d6be9ea317f92e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:01:33 +0200 Subject: [PATCH 72/76] Rename `ModSelect{Screen -> Overlay}Strings` --- ...ModSelectScreenStrings.cs => ModSelectOverlayStrings.cs} | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game/Localisation/{ModSelectScreenStrings.cs => ModSelectOverlayStrings.cs} (94%) diff --git a/osu.Game/Localisation/ModSelectScreenStrings.cs b/osu.Game/Localisation/ModSelectOverlayStrings.cs similarity index 94% rename from osu.Game/Localisation/ModSelectScreenStrings.cs rename to osu.Game/Localisation/ModSelectOverlayStrings.cs index 0c113fd381..e9af7147e3 100644 --- a/osu.Game/Localisation/ModSelectScreenStrings.cs +++ b/osu.Game/Localisation/ModSelectOverlayStrings.cs @@ -5,9 +5,9 @@ using osu.Framework.Localisation; namespace osu.Game.Localisation { - public static class ModSelectScreenStrings + public static class ModSelectOverlayStrings { - private const string prefix = @"osu.Game.Resources.Localisation.ModSelectScreen"; + private const string prefix = @"osu.Game.Resources.Localisation.ModSelectOverlay"; /// /// "Mod Select" @@ -26,4 +26,4 @@ namespace osu.Game.Localisation private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b589b2e7e1..7a8e89584a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -84,8 +84,8 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load(OsuColour colours) { - Header.Title = ModSelectScreenStrings.ModSelectTitle; - Header.Description = ModSelectScreenStrings.ModSelectDescription; + Header.Title = ModSelectOverlayStrings.ModSelectTitle; + Header.Description = ModSelectOverlayStrings.ModSelectDescription; AddRange(new Drawable[] { @@ -262,7 +262,7 @@ namespace osu.Game.Overlays.Mods { customisationButton = new ShearedToggleButton(BUTTON_WIDTH) { - Text = ModSelectScreenStrings.ModCustomisation, + Text = ModSelectOverlayStrings.ModCustomisation, Active = { BindTarget = customisationVisible } }, new ShearedButton(BUTTON_WIDTH) From 62f6caf76dcc836b08a7db054d7f77d149c551c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:04:39 +0200 Subject: [PATCH 73/76] Rename `ModSelect{Screen -> Overlay}` test scenes --- ...eFreeModSelectScreen.cs => TestSceneFreeModSelectOverlay.cs} | 2 +- ...TestSceneModSelectScreen.cs => TestSceneModSelectOverlay.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Multiplayer/{TestSceneFreeModSelectScreen.cs => TestSceneFreeModSelectOverlay.cs} (98%) rename osu.Game.Tests/Visual/UserInterface/{TestSceneModSelectScreen.cs => TestSceneModSelectOverlay.cs} (99%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs similarity index 98% rename from osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs rename to osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 9a24a1c646..f40c31b07f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -17,7 +17,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneFreeModSelectScreen : MultiplayerTestScene + public class TestSceneFreeModSelectOverlay : MultiplayerTestScene { private FreeModSelectOverlay freeModSelectOverlay; private readonly Bindable>> availableMods = new Bindable>>(); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs similarity index 99% rename from osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2e2ce6edd7..08ad030b39 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectScreen.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -24,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneModSelectScreen : OsuManualInputManagerTestScene + public class TestSceneModSelectOverlay : OsuManualInputManagerTestScene { [Resolved] private RulesetStore rulesetStore { get; set; } From 9b7ff9f2ee3d9aee2eb443c1e354024e8eae07ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:05:09 +0200 Subject: [PATCH 74/76] Rename `modSelect{Screen -> Overlay}2` variable --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 08ad030b39..fc543d9db7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -222,7 +222,7 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestSettingsNotCrossPolluting() { Bindable> selectedMods2 = null; - ModSelectOverlay modSelectScreen2 = null; + ModSelectOverlay modSelectOverlay2 = null; createScreen(); AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() }); @@ -235,7 +235,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("create second overlay", () => { - Add(modSelectScreen2 = new UserModSelectOverlay().With(d => + Add(modSelectOverlay2 = new UserModSelectOverlay().With(d => { d.Origin = Anchor.TopCentre; d.Anchor = Anchor.TopCentre; @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.UserInterface })); }); - AddStep("show", () => modSelectScreen2.Show()); + AddStep("show", () => modSelectOverlay2.Show()); AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType().Single().CircleSize.Value == 8); AddAssert("ensure second is default", () => selectedMods2.Value.OfType().Single().CircleSize.Value == null); From ddb2d4eef51220e0f41835ddfe4ec593482279fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 May 2022 18:06:09 +0200 Subject: [PATCH 75/76] Rename `FreeModSelect{Screen -> Overlay}` reference in inline comment --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7a8e89584a..b3c3eee15a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Mods State.BindValueChanged(_ => samplePlaybackDisabled.Value = State.Value == Visibility.Hidden, true); // This is an optimisation to prevent refreshing the available settings controls when it can be - // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectScreen). + // reasonably assumed that the settings panel is never to be displayed (e.g. FreeModSelectOverlay). if (customisationButton != null) ((IBindable>)modSettingsArea.SelectedMods).BindTo(SelectedMods); From b794deb5c544e5612745e1d33c32fa3122edd7e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 May 2022 12:06:51 +0900 Subject: [PATCH 76/76] Add null checks to screen context insertion --- osu.Game/OsuGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9edcf90132..3d56d33689 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -14,6 +14,7 @@ using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -1203,8 +1204,8 @@ namespace osu.Game { scope.Contexts[@"screen stack"] = new { - Current = newScreen.GetType().Name, - Previous = current.GetType().Name, + Current = newScreen?.GetType().ReadableName(), + Previous = current?.GetType().ReadableName(), }; });