1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-16 16:17:19 +08:00

Merge pull request #15190 from peppy/fix-password-popover-focus

Fix focus returning to the lounge search textbox while password is being checked
This commit is contained in:
Dan Balasescu 2021-10-22 23:16:25 +09:00 committed by GitHub
commit 8dc91809a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 201 additions and 15 deletions

View File

@ -0,0 +1,167 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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<LoungeSubScreen>();
mockLounge
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string>>()))
.Callback<Room, string, Action<Room>, Action<string>>((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<DrawableLoungeRoom.PasswordEntryPopover>().SingleOrDefault()) != null);
AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("enter password", () => popover.ChildrenOfType<OsuPasswordTextBox>().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<OsuPasswordTextBox>().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<DrawableLoungeRoom.PasswordEntryPopover>().SingleOrDefault()) != null);
AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("enter password", () => popover.ChildrenOfType<OsuPasswordTextBox>().Single().Text = "password");
AddStep("commit via click button", () =>
{
var button = popover.ChildrenOfType<OsuButton>().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<OsuPasswordTextBox>().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;
}
}

View File

@ -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 }
}
};
});

View File

@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public IEnumerable<string> 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);
}
});
}
}
}

View File

@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
popoverContainer.HidePopover();
}
public void Join(Room room, string password, Action<Room> onSuccess = null, Action<string> onFailure = null) => Schedule(() =>
public virtual void Join(Room room, string password, Action<Room> onSuccess = null, Action<string> onFailure = null) => Schedule(() =>
{
if (joiningRoomOperation != null)
return;