mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 03:25:11 +08:00
Merge pull request #18090 from peppy/playlist-keyboard-traversal
Add support for traversing playlist items using next/previous bindings
This commit is contained in:
commit
b9d8d1b4f6
@ -56,6 +56,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -73,6 +76,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -91,6 +97,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -147,6 +156,40 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyboardSelection()
|
||||
{
|
||||
createPlaylist(p => p.AllowSelection = true);
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("item 1 is selected", () => playlist.SelectedItem.Value == playlist.Items[1]);
|
||||
|
||||
AddStep("press up", () => InputManager.Key(Key.Up));
|
||||
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
|
||||
|
||||
AddUntilStep("navigate to last item via keyboard", () =>
|
||||
{
|
||||
InputManager.Key(Key.Down);
|
||||
return playlist.SelectedItem.Value == playlist.Items.Last();
|
||||
});
|
||||
AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last());
|
||||
AddUntilStep("last item is scrolled into view", () =>
|
||||
{
|
||||
var drawableItem = playlist.ItemMap[playlist.Items.Last()];
|
||||
return playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.TopLeft)
|
||||
&& playlist.ScreenSpaceDrawQuad.Contains(drawableItem.ScreenSpaceDrawQuad.BottomRight);
|
||||
});
|
||||
|
||||
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||
AddAssert("last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Last());
|
||||
|
||||
AddStep("press up", () => InputManager.Key(Key.Up));
|
||||
AddAssert("second last item is selected", () => playlist.SelectedItem.Value == playlist.Items.Reverse().ElementAt(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDownloadButtonHiddenWhenBeatmapExists()
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Chat;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
@ -119,6 +120,20 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
public class ChatTextBox : FocusedTextBox
|
||||
{
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
// Chat text boxes are generally used in places where they retain focus, but shouldn't block interaction with other
|
||||
// elements on the same screen.
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Up:
|
||||
case Key.Down:
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -6,7 +6,10 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK;
|
||||
|
||||
@ -15,7 +18,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// <summary>
|
||||
/// A scrollable list which displays the <see cref="PlaylistItem"/>s in a <see cref="Room"/>.
|
||||
/// </summary>
|
||||
public class DrawableRoomPlaylist : OsuRearrangeableListContainer<PlaylistItem>
|
||||
public class DrawableRoomPlaylist : OsuRearrangeableListContainer<PlaylistItem>, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently-selected item. Selection is visually represented with a border.
|
||||
@ -169,5 +172,78 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
});
|
||||
|
||||
protected virtual DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new DrawableRoomPlaylistItem(item);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// schedules added as the properties may change value while the drawable items haven't been created yet.
|
||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(scrollToSelection));
|
||||
Items.BindCollectionChanged((_, __) => Scheduler.AddOnce(scrollToSelection), true);
|
||||
}
|
||||
|
||||
private void scrollToSelection()
|
||||
{
|
||||
// SelectedItem and ItemMap/drawable items are managed separately,
|
||||
// so if the item can't be unmapped to a drawable, don't try to scroll to it.
|
||||
// best effort is made to not drop any updates, by subscribing to both sources.
|
||||
if (SelectedItem.Value == null || !ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem))
|
||||
return;
|
||||
|
||||
// ScrollIntoView does not handle non-loaded items appropriately, delay scroll until the item finishes loading.
|
||||
// see: https://github.com/ppy/osu-framework/issues/5158
|
||||
if (!drawableItem.IsLoaded)
|
||||
drawableItem.OnLoadComplete += _ => ScrollContainer.ScrollIntoView(drawableItem);
|
||||
else
|
||||
ScrollContainer.ScrollIntoView(drawableItem);
|
||||
}
|
||||
|
||||
#region Key selection logic (shared with BeatmapCarousel and RoomsContainer)
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
selectNext(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SelectPrevious:
|
||||
selectNext(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
private void selectNext(int direction)
|
||||
{
|
||||
if (!AllowSelection)
|
||||
return;
|
||||
|
||||
var visibleItems = ListContainer.AsEnumerable().Where(r => r.IsPresent);
|
||||
|
||||
PlaylistItem item;
|
||||
|
||||
if (SelectedItem.Value == null)
|
||||
item = visibleItems.FirstOrDefault()?.Model;
|
||||
else
|
||||
{
|
||||
if (direction < 0)
|
||||
visibleItems = visibleItems.Reverse();
|
||||
|
||||
item = visibleItems.SkipWhile(r => r.Model != SelectedItem.Value).Skip(1).FirstOrDefault()?.Model;
|
||||
}
|
||||
|
||||
// we already have a valid selection only change selection if we still have a room to switch to.
|
||||
if (item != null)
|
||||
SelectedItem.Value = item;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
#region Key selection logic (shared with BeatmapCarousel)
|
||||
#region Key selection logic (shared with BeatmapCarousel and DrawableRoomPlaylist)
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user