diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
index a899d072ac..74037dd3ec 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
@@ -133,6 +133,12 @@ namespace osu.Game.Tests.Visual.Navigation
return () => imported;
}
+ ///
+ /// Some tests test waiting for a particular screen twice in a row, but expect a new instance each time.
+ /// There's a case where they may succeed incorrectly if we don't compare against the previous instance.
+ ///
+ private IScreen lastWaitedScreen;
+
private void presentAndConfirm(Func getImport, ScorePresentType type)
{
AddStep("present score", () => Game.PresentScore(getImport(), type));
@@ -140,13 +146,15 @@ namespace osu.Game.Tests.Visual.Navigation
switch (type)
{
case ScorePresentType.Results:
- AddUntilStep("wait for results", () => Game.ScreenStack.CurrentScreen is ResultsScreen);
+ AddUntilStep("wait for results", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ResultsScreen);
+ AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ResultsScreen)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID);
break;
case ScorePresentType.Gameplay:
- AddUntilStep("wait for player loader", () => Game.ScreenStack.CurrentScreen is ReplayPlayerLoader);
+ AddUntilStep("wait for player loader", () => lastWaitedScreen != Game.ScreenStack.CurrentScreen && Game.ScreenStack.CurrentScreen is ReplayPlayerLoader);
+ AddStep("store last waited screen", () => lastWaitedScreen = Game.ScreenStack.CurrentScreen);
AddUntilStep("correct score displayed", () => ((ReplayPlayerLoader)Game.ScreenStack.CurrentScreen).Score.ID == getImport().ID);
AddAssert("correct ruleset selected", () => Game.Ruleset.Value.ID == getImport().Ruleset.ID);
break;
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
index 0901976af2..c96952431a 100644
--- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs
@@ -13,6 +13,7 @@ using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
+using osu.Game.Screens.Select.Options;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Input;
@@ -168,6 +169,29 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
}
+ [Test]
+ public void TestBeatmapOptionsInput()
+ {
+ TestSongSelect songSelect = null;
+
+ PushAndConfirm(() => songSelect = new TestSongSelect());
+
+ AddStep("Show options overlay", () => songSelect.BeatmapOptionsOverlay.Show());
+
+ AddStep("Change ruleset to osu!taiko", () =>
+ {
+ InputManager.PressKey(Key.ControlLeft);
+ InputManager.PressKey(Key.Number2);
+
+ InputManager.ReleaseKey(Key.ControlLeft);
+ InputManager.ReleaseKey(Key.Number2);
+ });
+
+ AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType().Single().Current.Value.ID == 1);
+
+ AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
+ }
+
private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape));
@@ -193,6 +217,8 @@ namespace osu.Game.Tests.Visual.Navigation
private class TestSongSelect : PlaySongSelect
{
public ModSelectOverlay ModSelectOverlay => ModSelect;
+
+ public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions;
}
}
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs
index f55c099d83..e9742acdde 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapOptionsOverlay.cs
@@ -3,9 +3,8 @@
using System.ComponentModel;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
using osu.Game.Screens.Select.Options;
-using osuTK.Graphics;
-using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
{
@@ -16,10 +15,13 @@ namespace osu.Game.Tests.Visual.SongSelect
{
var overlay = new BeatmapOptionsOverlay();
- overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, Color4.Purple, null, Key.Number1);
- overlay.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, Color4.Purple, null, Key.Number2);
- overlay.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, Color4.Pink, null, Key.Number3);
- overlay.AddButton(@"Edit", @"beatmap", FontAwesome.Solid.PencilAlt, Color4.Yellow, null, Key.Number4);
+ var colours = new OsuColour();
+
+ overlay.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, null);
+ overlay.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, null);
+ overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null);
+ overlay.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, null);
+ overlay.AddButton(@"Edit", @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, null);
Add(overlay);
diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
index 4e4653cb57..6e2f3cc9df 100644
--- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
+++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs
@@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
-using osuTK.Input;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select.Options
@@ -52,8 +51,6 @@ namespace osu.Game.Screens.Select.Options
set => secondLine.Text = value;
}
- public Key? HotKey;
-
protected override bool OnMouseDown(MouseDownEvent e)
{
flash.FadeTo(0.1f, 1000, Easing.OutQuint);
@@ -75,17 +72,6 @@ namespace osu.Game.Screens.Select.Options
return base.OnClick(e);
}
- protected override bool OnKeyDown(KeyDownEvent e)
- {
- if (!e.Repeat && e.Key == HotKey)
- {
- Click();
- return true;
- }
-
- return false;
- }
-
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
public BeatmapOptionsButton()
diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
index c01970f536..2676635764 100644
--- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
+++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs
@@ -11,6 +11,8 @@ using osuTK;
using osuTK.Graphics;
using osuTK.Input;
using osu.Game.Graphics.Containers;
+using osu.Framework.Input.Events;
+using System.Linq;
namespace osu.Game.Screens.Select.Options
{
@@ -27,33 +29,6 @@ namespace osu.Game.Screens.Select.Options
public override bool BlockScreenWideMouse => false;
- protected override void PopIn()
- {
- base.PopIn();
-
- this.FadeIn(transition_duration, Easing.OutQuint);
-
- if (buttonsContainer.Position.X == 1 || Alpha == 0)
- buttonsContainer.MoveToX(x_position - x_movement);
-
- holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, Easing.OutQuint);
-
- buttonsContainer.MoveToX(x_position, transition_duration, Easing.OutQuint);
- buttonsContainer.TransformSpacingTo(Vector2.Zero, transition_duration, Easing.OutQuint);
- }
-
- protected override void PopOut()
- {
- base.PopOut();
-
- holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, Easing.InSine);
-
- buttonsContainer.MoveToX(x_position + x_movement, transition_duration, Easing.InSine);
- buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, Easing.InSine);
-
- this.FadeOut(transition_duration, Easing.InQuint);
- }
-
public BeatmapOptionsOverlay()
{
AutoSizeAxes = Axes.Y;
@@ -87,9 +62,8 @@ namespace osu.Game.Screens.Select.Options
/// Text in the second line.
/// Colour of the button.
/// Icon of the button.
- /// Hotkey of the button.
/// Binding the button does.
- public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action, Key? hotkey = null)
+ public void AddButton(string firstLine, string secondLine, IconUsage icon, Color4 colour, Action action)
{
var button = new BeatmapOptionsButton
{
@@ -102,10 +76,58 @@ namespace osu.Game.Screens.Select.Options
Hide();
action?.Invoke();
},
- HotKey = hotkey
};
buttonsContainer.Add(button);
}
+
+ protected override void PopIn()
+ {
+ base.PopIn();
+
+ this.FadeIn(transition_duration, Easing.OutQuint);
+
+ if (buttonsContainer.Position.X == 1 || Alpha == 0)
+ buttonsContainer.MoveToX(x_position - x_movement);
+
+ holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, Easing.OutQuint);
+
+ buttonsContainer.MoveToX(x_position, transition_duration, Easing.OutQuint);
+ buttonsContainer.TransformSpacingTo(Vector2.Zero, transition_duration, Easing.OutQuint);
+ }
+
+ protected override void PopOut()
+ {
+ base.PopOut();
+
+ holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, Easing.InSine);
+
+ buttonsContainer.MoveToX(x_position + x_movement, transition_duration, Easing.InSine);
+ buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, Easing.InSine);
+
+ this.FadeOut(transition_duration, Easing.InQuint);
+ }
+
+ protected override bool OnKeyDown(KeyDownEvent e)
+ {
+ // don't absorb control as ToolbarRulesetSelector uses control + number to navigate
+ if (e.ControlPressed) return false;
+
+ if (!e.Repeat && e.Key >= Key.Number1 && e.Key <= Key.Number9)
+ {
+ int requested = e.Key - Key.Number1;
+
+ // go reverse as buttonsContainer is a ReverseChildIDFillFlowContainer
+ BeatmapOptionsButton found = buttonsContainer.Children.ElementAtOrDefault((buttonsContainer.Children.Count - 1) - requested);
+
+ if (found != null)
+ {
+ found.Click();
+ return true;
+ }
+ }
+
+ return base.OnKeyDown(e);
+ }
}
}
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 2236aa4d72..19769f487d 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select
{
ValidForResume = false;
Edit();
- }, Key.Number4);
+ });
((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += PresentScore;
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index d313f67446..a85e1869be 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Screens.Select
private MusicController music { get; set; }
[BackgroundDependencyLoader(true)]
- private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores, CollectionManager collections)
+ private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores, CollectionManager collections, ManageCollectionsDialog manageCollectionsDialog)
{
// initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter).
transferRulesetValue();
@@ -275,9 +275,10 @@ namespace osu.Game.Screens.Select
Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
- BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null, Key.Number1);
- BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2);
- BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number3);
+ BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
+ BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo));
+ BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null);
+ BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo));
}
dialogOverlay = dialog;
@@ -517,6 +518,8 @@ namespace osu.Game.Screens.Select
FilterControl.Activate();
ModSelect.SelectedMods.BindTo(selectedMods);
+
+ music.TrackChanged += ensureTrackLooping;
}
private const double logo_transition = 250;
@@ -568,6 +571,7 @@ namespace osu.Game.Screens.Select
BeatmapDetails.Refresh();
music.CurrentTrack.Looping = true;
+ music.TrackChanged += ensureTrackLooping;
music.ResetTrackAdjustments();
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
@@ -593,6 +597,7 @@ namespace osu.Game.Screens.Select
BeatmapOptions.Hide();
music.CurrentTrack.Looping = false;
+ music.TrackChanged -= ensureTrackLooping;
this.ScaleTo(1.1f, 250, Easing.InSine);
@@ -614,10 +619,14 @@ namespace osu.Game.Screens.Select
FilterControl.Deactivate();
music.CurrentTrack.Looping = false;
+ music.TrackChanged -= ensureTrackLooping;
return false;
}
+ private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection)
+ => music.CurrentTrack.Looping = true;
+
public override bool OnBackButton()
{
if (ModSelect.State.Value == Visibility.Visible)
@@ -634,6 +643,9 @@ namespace osu.Game.Screens.Select
base.Dispose(isDisposing);
decoupledRuleset.UnbindAll();
+
+ if (music != null)
+ music.TrackChanged -= ensureTrackLooping;
}
///
@@ -653,8 +665,6 @@ namespace osu.Game.Screens.Select
beatmapInfoWedge.Beatmap = beatmap;
BeatmapDetails.Beatmap = beatmap;
-
- music.CurrentTrack.Looping = true;
}
private readonly WeakReference lastTrack = new WeakReference(null);
diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs
index 2c46e7f6d3..aa3bd2e4b7 100644
--- a/osu.Game/Tests/Visual/PlayerTestScene.cs
+++ b/osu.Game/Tests/Visual/PlayerTestScene.cs
@@ -81,6 +81,12 @@ namespace osu.Game.Tests.Visual
LoadScreen(Player);
}
+ protected override void Dispose(bool isDisposing)
+ {
+ LocalConfig?.Dispose();
+ base.Dispose(isDisposing);
+ }
+
///
/// Creates the ruleset for setting up the component.
///