diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs index 66bf870f90..2fb8d11cd3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLoadingLayer.cs @@ -7,21 +7,27 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Tests.Visual.UserInterface { - public partial class TestSceneLoadingLayer : OsuTestScene + public partial class TestSceneLoadingLayer : OsuManualInputManagerTestScene { private TestLoadingLayer overlay; private Container content; + private PressableButton pressableButton; + [SetUp] public void SetUp() => Schedule(() => { @@ -51,10 +57,9 @@ namespace osu.Game.Tests.Visual.UserInterface { new OsuSpriteText { Text = "Sample content" }, new RoundedButton { Text = "can't puush me", Width = 200, }, - new RoundedButton { Text = "puush me", Width = 200, Action = () => { } }, + pressableButton = new PressableButton { Text = "puush me", Width = 200 }, } }, - overlay = new TestLoadingLayer(true), } }, }; @@ -63,20 +68,62 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestShowHide() { + AddStep("create loading layer", () => content.Add(overlay = new TestLoadingLayer(true))); + AddAssert("not visible", () => !overlay.IsPresent); AddStep("show", () => overlay.Show()); - AddUntilStep("wait for content dim", () => overlay.Alpha > 0); AddStep("hide", () => overlay.Hide()); - AddUntilStep("wait for content restore", () => Precision.AlmostEquals(overlay.Alpha, 0)); } + [TestCase(true)] + [TestCase(false)] + public void TestBlockPositional(bool blockInput) + { + AddStep("create loading layer", () => content.Add(overlay = new TestLoadingLayer(true) { BlockPositionalInput = blockInput })); + AddStep("show", () => overlay.Show()); + + AddStep("click button", () => + { + InputManager.MoveMouseTo(pressableButton); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("check pressed", () => pressableButton.Pressed, () => Is.EqualTo(!blockInput)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBlockNonPositional(bool blockKeyboardInput) + { + AddStep("create loading layer", () => content.Add(overlay = new TestLoadingLayer(true) { BlockNonPositionalInput = blockKeyboardInput })); + AddStep("show", () => overlay.Show()); + + AddStep("press enter", () => InputManager.Key(Key.Enter)); + + AddAssert("check pressed", () => pressableButton.Pressed, () => Is.EqualTo(!blockKeyboardInput)); + } + + [TestCase(true)] + [TestCase(false)] + public void TestBlockNonPositionalGlobalAction(bool blockKeyboardInput) + { + AddStep("create loading layer", () => content.Add(overlay = new TestLoadingLayer(true) { BlockNonPositionalInput = blockKeyboardInput })); + AddStep("show", () => overlay.Show()); + + AddStep("press enter", () => InputManager.Key(Key.F8)); + + AddAssert("check pressed", () => pressableButton.Pressed, () => Is.EqualTo(!blockKeyboardInput)); + } + [Test] public void TestLargeArea() { + AddStep("create loading layer", () => content.Add(overlay = new TestLoadingLayer(true))); + AddStep("show", () => { content.RelativeSizeAxes = Axes.Both; @@ -88,6 +135,42 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("hide", () => overlay.Hide()); } + public partial class PressableButton : RoundedButton, IKeyBindingHandler + { + public PressableButton() + { + Action = () => Pressed = true; + } + + public bool Pressed { get; private set; } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key == Key.Enter) + { + Pressed = true; + return true; + } + + return base.OnKeyDown(e); + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == GlobalAction.ToggleChat) + { + Pressed = true; + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + private partial class TestLoadingLayer : LoadingLayer { public TestLoadingLayer(bool dimBackground = false, bool withBox = true) diff --git a/osu.Game/Graphics/UserInterface/LoadingLayer.cs b/osu.Game/Graphics/UserInterface/LoadingLayer.cs index 916b041696..0a5801475e 100644 --- a/osu.Game/Graphics/UserInterface/LoadingLayer.cs +++ b/osu.Game/Graphics/UserInterface/LoadingLayer.cs @@ -6,7 +6,9 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -17,20 +19,28 @@ namespace osu.Game.Graphics.UserInterface /// Also optionally dims target elements. /// Useful for disabling all elements in a form and showing we are waiting on a response, for instance. /// - public partial class LoadingLayer : LoadingSpinner + public partial class LoadingLayer : LoadingSpinner, IKeyBindingHandler { - private readonly bool blockInput; + /// + /// Whether to block positional input of components behind the loading layer. + /// Defaults to true. + /// + public bool BlockPositionalInput { get; init; } = true; + + /// + /// Whether to block all keyboard input. Includes global actions. + /// Defaults to false. + /// + public bool BlockNonPositionalInput { get; init; } /// /// Construct a new loading spinner. /// /// Whether the full background area should be dimmed while loading. /// Whether the spinner should have a surrounding black box for visibility. - /// Whether to block input of components behind the loading layer. - public LoadingLayer(bool dimBackground = false, bool withBox = true, bool blockInput = true) + public LoadingLayer(bool dimBackground = false, bool withBox = true) : base(withBox) { - this.blockInput = blockInput; RelativeSizeAxes = Axes.Both; Size = new Vector2(1); @@ -48,11 +58,11 @@ namespace osu.Game.Graphics.UserInterface } } - public override bool HandleNonPositionalInput => false; + public override bool HandleNonPositionalInput => BlockNonPositionalInput; protected override bool Handle(UIEvent e) { - if (!blockInput) + if (!BlockPositionalInput) return false; switch (e) @@ -76,5 +86,11 @@ namespace osu.Game.Graphics.UserInterface MainContents.Size = new Vector2(Math.Clamp(Math.Min(DrawWidth, DrawHeight) * 0.25f, 20, 80)); } + + public bool OnPressed(KeyBindingPressEvent e) => BlockNonPositionalInput; + + public void OnReleased(KeyBindingReleaseEvent e) + { + } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 787c525566..2ade335ef7 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -59,8 +59,9 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Both, }, - spinner = new LoadingLayer(dimBackground: true, withBox: false, blockInput: false) + spinner = new LoadingLayer(dimBackground: true, withBox: false) { + BlockPositionalInput = false, Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs index 23264c4518..f1df01b743 100644 --- a/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs +++ b/osu.Game/Screens/Play/BeatmapMetadataDisplay.cs @@ -114,7 +114,10 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, FillMode = FillMode.Fill, }, - loading = new LoadingLayer(dimBackground: true, blockInput: false) + loading = new LoadingLayer(dimBackground: true) + { + BlockPositionalInput = false, + } } }, versionFlow = new FillFlowContainer