1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-22 19:41:06 +08:00

Add ability for LoadingLayer to block all keyboard input

This commit is contained in:
Dean Herbert
2026-02-18 19:43:17 +09:00
Unverified
parent 110f11fa5e
commit d33a6d5512
4 changed files with 117 additions and 14 deletions
@@ -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<GlobalAction>
{
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<GlobalAction> e)
{
if (e.Action == GlobalAction.ToggleChat)
{
Pressed = true;
return true;
}
return false;
}
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}
}
private partial class TestLoadingLayer : LoadingLayer
{
public TestLoadingLayer(bool dimBackground = false, bool withBox = true)
@@ -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.
/// </summary>
public partial class LoadingLayer : LoadingSpinner
public partial class LoadingLayer : LoadingSpinner, IKeyBindingHandler<GlobalAction>
{
private readonly bool blockInput;
/// <summary>
/// Whether to block positional input of components behind the loading layer.
/// Defaults to <c>true</c>.
/// </summary>
public bool BlockPositionalInput { get; init; } = true;
/// <summary>
/// Whether to block all keyboard input. Includes global actions.
/// Defaults to <c>false</c>.
/// </summary>
public bool BlockNonPositionalInput { get; init; }
/// <summary>
/// Construct a new loading spinner.
/// </summary>
/// <param name="dimBackground">Whether the full background area should be dimmed while loading.</param>
/// <param name="withBox">Whether the spinner should have a surrounding black box for visibility.</param>
/// <param name="blockInput">Whether to block input of components behind the loading layer.</param>
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<GlobalAction> e) => BlockNonPositionalInput;
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}
}
}
@@ -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,
@@ -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