diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs new file mode 100644 index 0000000000..ea895a23d2 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs @@ -0,0 +1,167 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Rooms; +using osu.Game.Overlays; +using osu.Game.Screens.OnlinePlay.Lounge; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneDrawableLoungeRoom : OsuManualInputManagerTestScene + { + private readonly Room room = new Room + { + HasPassword = { Value = true } + }; + + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + private DrawableLoungeRoom drawableRoom; + private SearchTextBox searchTextBox; + + private readonly ManualResetEventSlim allowResponseCallback = new ManualResetEventSlim(); + + [BackgroundDependencyLoader] + private void load() + { + var mockLounge = new Mock(); + mockLounge + .Setup(l => l.Join(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>())) + .Callback, Action>((a, b, c, d) => + { + Task.Run(() => + { + allowResponseCallback.Wait(); + allowResponseCallback.Reset(); + Schedule(() => d?.Invoke("Incorrect password")); + }); + }); + + Dependencies.CacheAs(mockLounge.Object); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create drawable", () => + { + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + searchTextBox = new SearchTextBox + { + HoldFocus = true, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding(50), + Width = 500, + Depth = float.MaxValue + }, + drawableRoom = new DrawableLoungeRoom(room) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + }); + } + + [Test] + public void TestFocusViaKeyboardCommit() + { + DrawableLoungeRoom.PasswordEntryPopover popover = null; + + AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); + AddStep("click room twice", () => + { + InputManager.MoveMouseTo(drawableRoom); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType().SingleOrDefault()) != null); + + AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("enter password", () => popover.ChildrenOfType().Single().Text = "password"); + AddStep("commit via enter", () => InputManager.Key(Key.Enter)); + + AddAssert("popover has focus", () => checkFocus(popover)); + + AddStep("attempt another enter", () => InputManager.Key(Key.Enter)); + + AddAssert("popover still has focus", () => checkFocus(popover)); + + AddStep("unblock response", () => allowResponseCallback.Set()); + + AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("press escape", () => InputManager.Key(Key.Escape)); + AddStep("press escape", () => InputManager.Key(Key.Escape)); + + AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox)); + } + + [Test] + public void TestFocusViaMouseCommit() + { + DrawableLoungeRoom.PasswordEntryPopover popover = null; + + AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); + AddStep("click room twice", () => + { + InputManager.MoveMouseTo(drawableRoom); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType().SingleOrDefault()) != null); + + AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("enter password", () => popover.ChildrenOfType().Single().Text = "password"); + + AddStep("commit via click button", () => + { + var button = popover.ChildrenOfType().Single(); + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("popover has focus", () => checkFocus(popover)); + + AddStep("attempt another click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("popover still has focus", () => checkFocus(popover)); + + AddStep("unblock response", () => allowResponseCallback.Set()); + + AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("click away", () => + { + InputManager.MoveMouseTo(searchTextBox); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox)); + } + + private bool checkFocus(Drawable expected) => + InputManager.FocusedDrawable == expected; + } +} diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 99b530c2a2..1bdf3c2750 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; @@ -25,12 +26,18 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - Child = container = new RoomsContainer + Child = new PopoverContainer { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 0.5f, - SelectedRoom = { BindTarget = SelectedRoom } + + Child = container = new RoomsContainer + { + SelectedRoom = { BindTarget = SelectedRoom } + } }; }); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index fe7c7cc364..72574b729a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public IEnumerable FilterTerms => new[] { Room.Name.Value }; - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { @@ -181,6 +181,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } + public override bool HandleNonPositionalInput => true; + + protected override bool BlockNonPositionalInput => true; + public PasswordEntryPopover(Room room) { this.room = room; @@ -200,6 +204,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, + LayoutDuration = 500, + LayoutEasing = Easing.OutQuint, Children = new Drawable[] { new FillFlowContainer @@ -230,10 +236,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge sampleJoinFail = audio.Samples.Get(@"UI/password-fail"); - joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + joinButton.Action = performJoin; } - private void joinFailed(string error) + protected override void LoadComplete() + { + base.LoadComplete(); + + Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); + passwordTextbox.OnCommit += (_, __) => performJoin(); + } + + private void performJoin() + { + lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + GetContainingInputManager().TriggerFocusContention(passwordTextbox); + } + + private void joinFailed(string error) => Schedule(() => { passwordTextbox.Text = string.Empty; @@ -249,15 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Body.Shake(); sampleJoinFail?.Play(); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); - } + }); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 62012906a7..cd1c8a0a64 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge popoverContainer.HidePopover(); } - public void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => + public virtual void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => { if (joiningRoomOperation != null) return;