diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
index 163dd7fbe9..59ff7b06e5 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
@@ -40,6 +40,7 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapSetInfo BeatmapSet;
private BeatmapGroupState state;
+
public BeatmapGroupState State
{
get { return state; }
@@ -52,7 +53,8 @@ namespace osu.Game.Beatmaps.Drawables
case BeatmapGroupState.Expanded:
Header.State = PanelSelectedState.Selected;
foreach (BeatmapPanel panel in BeatmapPanels)
- panel.State = panel == SelectedPanel ? PanelSelectedState.Selected : PanelSelectedState.NotSelected;
+ panel.State = panel == SelectedPanel ? PanelSelectedState.Selected :
+ !panel.Filtered ? PanelSelectedState.NotSelected : PanelSelectedState.Hidden;
break;
case BeatmapGroupState.Collapsed:
Header.State = PanelSelectedState.NotSelected;
@@ -108,7 +110,7 @@ namespace osu.Game.Beatmaps.Drawables
//we want to make sure one of our children is selected in the case none have been selected yet.
if (SelectedPanel == null)
- BeatmapPanels.First().State = PanelSelectedState.Selected;
+ BeatmapPanels.First(p => !p.Filtered).State = PanelSelectedState.Selected;
}
private void panelGainedSelection(BeatmapPanel panel)
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
index e6bf08eb9f..c94f4fdc57 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
@@ -61,6 +61,8 @@ namespace osu.Game.Beatmaps.Drawables
return base.OnClick(state);
}
+ public bool Filtered { get; set; }
+
protected override void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden)
{
if (!IsLoaded) return;
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 578b02d90a..fb6761eb8d 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -131,7 +131,7 @@ namespace osu.Game.Screens.Select
var newSelection =
newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID);
- if(newSelection == null && oldGroup != null && selectedPanel != null)
+ if (newSelection == null && oldGroup != null && selectedPanel != null)
newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, oldGroup.BeatmapPanels.IndexOf(selectedPanel))];
selectGroup(newGroup, newSelection);
@@ -178,42 +178,62 @@ namespace osu.Game.Screens.Select
SelectionChanged?.Invoke(null);
}
+ ///
+ /// Increment selection in the carousel in a chosen direction.
+ ///
+ /// The direction to increment. Negative is backwards.
+ /// Whether to skip individual difficulties and only increment over full groups.
public void SelectNext(int direction = 1, bool skipDifficulties = true)
{
+ // todo: we may want to refactor and remove this as an optimisation in the future.
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
{
selectNullBeatmap();
return;
}
- if (!skipDifficulties && selectedGroup != null)
- {
- int i = selectedGroup.BeatmapPanels.IndexOf(selectedPanel) + direction;
+ int originalIndex = Math.Max(0, groups.IndexOf(selectedGroup));
+ int currentIndex = originalIndex;
- if (i >= 0 && i < selectedGroup.BeatmapPanels.Count)
- {
- //changing difficulty panel, not set.
- selectGroup(selectedGroup, selectedGroup.BeatmapPanels[i]);
- return;
- }
- }
+ // local function to increment the index in the required direction, wrapping over extremities.
+ int incrementIndex() => currentIndex = (currentIndex + direction + groups.Count) % groups.Count;
- int startIndex = Math.Max(0, groups.IndexOf(selectedGroup));
- int index = startIndex;
+ // in the case we are skipping difficulties, we want to increment the index once before starting to find out new target
+ // (we don't care about the currently selected group).
+ if (skipDifficulties)
+ incrementIndex();
do
{
- index = (index + direction + groups.Count) % groups.Count;
- if (groups[index].State != BeatmapGroupState.Hidden)
- {
- if (skipDifficulties)
- SelectBeatmap(groups[index].SelectedPanel != null ? groups[index].SelectedPanel.Beatmap : groups[index].BeatmapPanels.First().Beatmap);
- else
- SelectBeatmap(direction == 1 ? groups[index].BeatmapPanels.First().Beatmap : groups[index].BeatmapPanels.Last().Beatmap);
+ var group = groups[currentIndex];
+ if (group.State == BeatmapGroupState.Hidden) continue;
+
+ // we are only interested in non-filtered panels.
+ IEnumerable validPanels = group.BeatmapPanels.Where(p => !p.Filtered);
+
+ // if we are considering difficulties, we need to do a few extrea steps.
+ if (!skipDifficulties)
+ {
+ // we want to reverse the panel order if we are searching backwards.
+ if (direction < 0)
+ validPanels = validPanels.Reverse();
+
+ // if we are currently on the selected panel, let's try to find a valid difficulty before leaving to the next group.
+ // the first valid difficulty is found by skipping to the selected panel and then one further.
+ if (currentIndex == originalIndex)
+ validPanels = validPanels.SkipWhile(p => p != selectedPanel).Skip(1);
+ }
+
+ var next = validPanels.FirstOrDefault();
+
+ // at this point, we can perform the selection change if we have a valid new target, else continue to increment in the specified direction.
+ if (next != null)
+ {
+ selectGroup(group, next);
return;
}
- } while (index != startIndex);
+ } while (incrementIndex() != originalIndex);
}
private IEnumerable getVisibleGroups() => groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden);
@@ -416,6 +436,8 @@ namespace osu.Game.Screens.Select
foreach (BeatmapPanel panel in group.BeatmapPanels)
{
+ if (panel.Filtered) continue;
+
if (panel == selectedPanel)
selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2;
@@ -460,7 +482,7 @@ namespace osu.Game.Screens.Select
try
{
if (panel == null)
- panel = group.BeatmapPanels.First();
+ panel = group.BeatmapPanels.First(p => !p.Filtered);
if (selectedPanel == panel) return;
diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs
index c1355bfa63..96778291a1 100644
--- a/osu.Game/Screens/Select/FilterCriteria.cs
+++ b/osu.Game/Screens/Select/FilterCriteria.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Rulesets;
using osu.Game.Screens.Select.Filter;
@@ -18,19 +19,26 @@ namespace osu.Game.Screens.Select
public RulesetInfo Ruleset;
public bool AllowConvertedBeatmaps;
+ private bool canConvert(BeatmapInfo beatmapInfo) => beatmapInfo.RulesetID == Ruleset.ID || beatmapInfo.RulesetID == 0 && Ruleset.ID > 0 && AllowConvertedBeatmaps;
+
public void Filter(List groups)
{
foreach (var g in groups)
{
var set = g.BeatmapSet;
- bool hasCurrentMode = AllowConvertedBeatmaps || set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
+ // we only support converts from osu! mode to other modes for now.
+ // in the future this will have to change, at which point this condition will become a touch more complicated.
+ bool hasCurrentMode = set.Beatmaps.Any(canConvert);
bool match = hasCurrentMode;
if (!string.IsNullOrEmpty(SearchText))
match &= set.Metadata.SearchableTerms.Any(term => term.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0);
+ foreach (var panel in g.BeatmapPanels)
+ panel.Filtered = !canConvert(panel.Beatmap);
+
switch (g.State)
{
case BeatmapGroupState.Hidden:
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 76929dcbb3..2f52881d6d 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -602,6 +602,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-frame
<Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
<Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy>
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />