Add creator= and artist= filters

To match stable, add creator= and artist= filters to the beatmap
carousel on song select screen. Contrary to stable, this implementation
supports phrase queries with spaces within using double quotes.

The quote handling is not entirely correct (can't nest), but quotes
should rarely happen within names, and it is an edge case of an edge
case - leaving best-effort as is. Test coverage also included.
This commit is contained in:
Bartłomiej Dach
2019-09-21 23:16:23 +02:00
parent 51509f6be0
commit b262ba13cd
5 changed files with 157 additions and 4 deletions

View File

@ -39,6 +39,10 @@ namespace osu.Game.Screens.Select.Carousel
match &= criteria.BeatDivisor.IsInRange(Beatmap.BeatDivisor);
match &= criteria.OnlineStatus.IsInRange(Beatmap.Status);
match &= criteria.Creator.Matches(Beatmap.Metadata.AuthorString);
match &= criteria.Artist.Matches(Beatmap.Metadata.Artist) ||
criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode);
if (match)
foreach (var criteriaTerm in criteria.SearchTerms)
match &=

View File

@ -23,6 +23,8 @@ namespace osu.Game.Screens.Select
public OptionalRange<double> BPM;
public OptionalRange<int> BeatDivisor;
public OptionalRange<BeatmapSetOnlineStatus> OnlineStatus;
public OptionalTextFilter Creator;
public OptionalTextFilter Artist;
public string[] SearchTerms = Array.Empty<string>();
@ -82,5 +84,24 @@ namespace osu.Game.Screens.Select
&& IsLowerInclusive.Equals(other.IsLowerInclusive)
&& IsUpperInclusive.Equals(other.IsUpperInclusive);
}
public struct OptionalTextFilter : IEquatable<OptionalTextFilter>
{
public bool Matches(string value)
{
if (string.IsNullOrEmpty(SearchTerm))
return true;
// search term is guaranteed to be non-empty, so if the string we're comparing is empty, it's not matching
if (string.IsNullOrEmpty(value))
return false;
return value.IndexOf(SearchTerm, StringComparison.InvariantCultureIgnoreCase) >= 0;
}
public string SearchTerm;
public bool Equals(OptionalTextFilter other) => SearchTerm?.Equals(other.SearchTerm) ?? true;
}
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Screens.Select
internal static class FilterQueryParser
{
private static readonly Regex query_syntax_regex = new Regex(
@"\b(?<key>stars|ar|dr|cs|divisor|length|objects|bpm|status)(?<op>[=:><]+)(?<value>\S*)",
@"\b(?<key>stars|ar|dr|cs|divisor|length|objects|bpm|status|creator|artist)(?<op>[=:><]+)(?<value>("".*"")|(\S*))",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
internal static void ApplyQueries(FilterCriteria criteria, string query)
@ -61,6 +61,14 @@ namespace osu.Game.Screens.Select
case "status" when Enum.TryParse<BeatmapSetOnlineStatus>(value, true, out var statusValue):
updateCriteriaRange(ref criteria.OnlineStatus, op, statusValue);
break;
case "creator":
updateCriteriaText(ref criteria.Creator, op, value);
break;
case "artist":
updateCriteriaText(ref criteria.Artist, op, value);
break;
}
query = query.Replace(match.ToString(), "");
@ -78,6 +86,17 @@ namespace osu.Game.Screens.Select
private static bool parseInt(string value, out int result) =>
int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result);
private static void updateCriteriaText(ref FilterCriteria.OptionalTextFilter textFilter, string op, string value)
{
switch (op)
{
case "=":
case ":":
textFilter.SearchTerm = value.Trim('"');
break;
}
}
private static void updateCriteriaRange(ref FilterCriteria.OptionalRange<float> range, string op, float value, float tolerance = 0.05f)
{
updateCriteriaRange(ref range, op, value);