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

Allow keyboard selection of rooms at the multiplayer lounge

This commit is contained in:
Dean Herbert 2020-07-09 17:15:16 +09:00
parent 95096cbf5e
commit 601101147e

View File

@ -9,13 +9,17 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Threading;
using osu.Game.Extensions;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osuTK; using osuTK;
namespace osu.Game.Screens.Multi.Lounge.Components namespace osu.Game.Screens.Multi.Lounge.Components
{ {
public class RoomsContainer : CompositeDrawable public class RoomsContainer : CompositeDrawable, IKeyBindingHandler<GlobalAction>
{ {
public Action<Room> JoinRequested; public Action<Room> JoinRequested;
@ -88,8 +92,22 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private void addRooms(IEnumerable<Room> rooms) private void addRooms(IEnumerable<Room> rooms)
{ {
foreach (var r in rooms) foreach (var room in rooms)
roomFlow.Add(new DrawableRoom(r) { Action = () => selectRoom(r) }); {
roomFlow.Add(new DrawableRoom(room)
{
Action = () =>
{
if (room == selectedRoom.Value)
{
JoinRequested?.Invoke(room);
return;
}
selectRoom(room);
}
});
}
Filter(filter?.Value); Filter(filter?.Value);
} }
@ -115,16 +133,89 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private void selectRoom(Room room) private void selectRoom(Room room)
{ {
var drawable = roomFlow.FirstOrDefault(r => r.Room == room);
if (drawable != null && drawable.State == SelectionState.Selected)
JoinRequested?.Invoke(room);
else
roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected); roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected);
selectedRoom.Value = room; selectedRoom.Value = room;
} }
#region Key selection logic
public bool OnPressed(GlobalAction action)
{
switch (action)
{
case GlobalAction.SelectNext:
beginRepeatSelection(() => selectNext(1), action);
return true;
case GlobalAction.SelectPrevious:
beginRepeatSelection(() => selectNext(-1), action);
return true;
}
return false;
}
public void OnReleased(GlobalAction action)
{
switch (action)
{
case GlobalAction.SelectNext:
case GlobalAction.SelectPrevious:
endRepeatSelection(action);
break;
}
}
private ScheduledDelegate repeatDelegate;
private object lastRepeatSource;
/// <summary>
/// Begin repeating the specified selection action.
/// </summary>
/// <param name="action">The action to perform.</param>
/// <param name="source">The source of the action. Used in conjunction with <see cref="endRepeatSelection"/> to only cancel the correct action (most recently pressed key).</param>
private void beginRepeatSelection(Action action, object source)
{
endRepeatSelection();
lastRepeatSource = source;
repeatDelegate = this.BeginKeyRepeat(Scheduler, action);
}
private void endRepeatSelection(object source = null)
{
// only the most recent source should be able to cancel the current action.
if (source != null && !EqualityComparer<object>.Default.Equals(lastRepeatSource, source))
return;
repeatDelegate?.Cancel();
repeatDelegate = null;
lastRepeatSource = null;
}
private void selectNext(int direction)
{
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
Room room;
if (selectedRoom.Value == null)
room = visibleRooms.FirstOrDefault()?.Room;
else
{
if (direction < 0)
visibleRooms = visibleRooms.Reverse();
room = visibleRooms.SkipWhile(r => r.Room != selectedRoom.Value).Skip(1).FirstOrDefault()?.Room;
}
// we already have a valid selection only change selection if we still have a room to switch to.
if (room != null)
selectRoom(room);
}
#endregion
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);