mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 09:47:52 +08:00
Add query-based filter modes to song select search field
This commit is contained in:
parent
52fcd834ab
commit
92556db9cd
@ -301,6 +301,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
var ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||||
beatmap.BeatmapInfo.Ruleset = ruleset;
|
beatmap.BeatmapInfo.Ruleset = ruleset;
|
||||||
|
|
||||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||||
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
|
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
|
||||||
beatmap.BeatmapInfo.Length = calculateLength(beatmap);
|
beatmap.BeatmapInfo.Length = calculateLength(beatmap);
|
||||||
|
@ -24,12 +24,26 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
{
|
{
|
||||||
base.Filter(criteria);
|
base.Filter(criteria);
|
||||||
|
|
||||||
bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
|
bool match =
|
||||||
|
criteria.Ruleset == null ||
|
||||||
|
Beatmap.RulesetID == criteria.Ruleset.ID ||
|
||||||
|
(Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
|
||||||
|
|
||||||
foreach (var criteriaTerm in criteria.SearchTerms)
|
match &= criteria.StarDifficulty.IsInRange(Beatmap.StarDifficulty);
|
||||||
match &=
|
match &= criteria.ApproachRate.IsInRange(Beatmap.BaseDifficulty.ApproachRate);
|
||||||
Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) ||
|
match &= criteria.DrainRate.IsInRange(Beatmap.BaseDifficulty.DrainRate);
|
||||||
Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0;
|
match &= criteria.CircleSize.IsInRange(Beatmap.BaseDifficulty.CircleSize);
|
||||||
|
match &= criteria.Length.IsInRange(Beatmap.Length);
|
||||||
|
match &= criteria.BPM.IsInRange(Beatmap.BPM);
|
||||||
|
|
||||||
|
match &= !criteria.BeatDivisor.HasValue || criteria.BeatDivisor == Beatmap.BeatDivisor;
|
||||||
|
match &= !criteria.OnlineStatus.HasValue || criteria.OnlineStatus == Beatmap.Status;
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
foreach (var criteriaTerm in criteria.SearchTerms)
|
||||||
|
match &=
|
||||||
|
Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) ||
|
||||||
|
Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0;
|
||||||
|
|
||||||
Filtered.Value = !match;
|
Filtered.Value = !match;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ using Container = osu.Framework.Graphics.Containers.Container;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
{
|
{
|
||||||
@ -33,14 +35,24 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private Bindable<GroupMode> groupMode;
|
private Bindable<GroupMode> groupMode;
|
||||||
|
|
||||||
public FilterCriteria CreateCriteria() => new FilterCriteria
|
public FilterCriteria CreateCriteria()
|
||||||
{
|
{
|
||||||
Group = groupMode.Value,
|
var query = searchTextBox.Text;
|
||||||
Sort = sortMode.Value,
|
|
||||||
SearchText = searchTextBox.Text,
|
var criteria = new FilterCriteria
|
||||||
AllowConvertedBeatmaps = showConverted.Value,
|
{
|
||||||
Ruleset = ruleset.Value
|
Group = groupMode.Value,
|
||||||
};
|
Sort = sortMode.Value,
|
||||||
|
AllowConvertedBeatmaps = showConverted.Value,
|
||||||
|
Ruleset = ruleset.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
applyQueries(criteria, ref query);
|
||||||
|
|
||||||
|
criteria.SearchText = query;
|
||||||
|
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
public Action Exit;
|
public Action Exit;
|
||||||
|
|
||||||
@ -169,5 +181,97 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
|
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
|
||||||
|
|
||||||
|
private static readonly Regex query_syntax_regex = new Regex(
|
||||||
|
@"\b(?<key>stars|ar|dr|cs|divisor|length|objects|bpm|status)(?<op>[:><]+)(?<value>\S*)",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
private void applyQueries(FilterCriteria criteria, ref string query)
|
||||||
|
{
|
||||||
|
foreach (Match match in query_syntax_regex.Matches(query))
|
||||||
|
{
|
||||||
|
var key = match.Groups["key"].Value.ToLower();
|
||||||
|
var op = match.Groups["op"].Value;
|
||||||
|
var value = match.Groups["value"].Value;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case "stars" when double.TryParse(value, out var stars):
|
||||||
|
updateCriteriaRange(ref criteria.StarDifficulty, op, stars, 0.5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ar" when double.TryParse(value, out var ar):
|
||||||
|
updateCriteriaRange(ref criteria.ApproachRate, op, ar, 0.3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "dr" when double.TryParse(value, out var dr):
|
||||||
|
updateCriteriaRange(ref criteria.DrainRate, op, dr, 0.3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "cs" when double.TryParse(value, out var cs):
|
||||||
|
updateCriteriaRange(ref criteria.CircleSize, op, cs, 0.3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "bpm" when double.TryParse(value, out var bpm):
|
||||||
|
updateCriteriaRange(ref criteria.BPM, op, bpm, 0.3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "length" when double.TryParse(value.TrimEnd('m', 's', 'h'), out var length):
|
||||||
|
var scale =
|
||||||
|
value.EndsWith("ms") ? 1 :
|
||||||
|
value.EndsWith("s") ? 1000 :
|
||||||
|
value.EndsWith("m") ? 60000 :
|
||||||
|
value.EndsWith("h") ? 3600000 : 1000;
|
||||||
|
|
||||||
|
updateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "divisor" when op == ":" && int.TryParse(value, out var divisor):
|
||||||
|
criteria.BeatDivisor = divisor;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "status" when op == ":" && Enum.TryParse<BeatmapSetOnlineStatus>(value, ignoreCase: true, out var statusValue):
|
||||||
|
criteria.OnlineStatus = statusValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query.Remove(match.Index, match.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCriteriaRange(ref FilterCriteria.OptionalRange range, string op, double value, double equalityToleration = 0)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ":":
|
||||||
|
range.IsInclusive = true;
|
||||||
|
range.Min = value - equalityToleration;
|
||||||
|
range.Max = value + equalityToleration;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ">":
|
||||||
|
range.IsInclusive = false;
|
||||||
|
range.Min = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ">:":
|
||||||
|
range.IsInclusive = true;
|
||||||
|
range.Min = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "<":
|
||||||
|
range.IsInclusive = false;
|
||||||
|
range.Max = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "<:":
|
||||||
|
range.IsInclusive = true;
|
||||||
|
range.Max = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Select.Filter;
|
using osu.Game.Screens.Select.Filter;
|
||||||
|
|
||||||
@ -13,6 +14,17 @@ namespace osu.Game.Screens.Select
|
|||||||
public GroupMode Group;
|
public GroupMode Group;
|
||||||
public SortMode Sort;
|
public SortMode Sort;
|
||||||
|
|
||||||
|
public OptionalRange StarDifficulty;
|
||||||
|
public OptionalRange ApproachRate;
|
||||||
|
public OptionalRange DrainRate;
|
||||||
|
public OptionalRange CircleSize;
|
||||||
|
public OptionalRange Length;
|
||||||
|
public OptionalRange BPM;
|
||||||
|
|
||||||
|
public int? BeatDivisor;
|
||||||
|
|
||||||
|
public BeatmapSetOnlineStatus? OnlineStatus;
|
||||||
|
|
||||||
public string[] SearchTerms = Array.Empty<string>();
|
public string[] SearchTerms = Array.Empty<string>();
|
||||||
|
|
||||||
public RulesetInfo Ruleset;
|
public RulesetInfo Ruleset;
|
||||||
@ -26,8 +38,27 @@ namespace osu.Game.Screens.Select
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
searchText = value;
|
searchText = value;
|
||||||
SearchTerms = searchText.Split(',', ' ', '!').Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
SearchTerms = searchText.Split(new[] { ',', ' ', '!' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct OptionalRange : IEquatable<OptionalRange>
|
||||||
|
{
|
||||||
|
public bool IsInRange(double value)
|
||||||
|
{
|
||||||
|
if (Min.HasValue && (IsInclusive ? value < Min.Value : value <= Min.Value))
|
||||||
|
return false;
|
||||||
|
if (Max.HasValue && (IsInclusive ? value > Max.Value : value >= Max.Value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double? Min;
|
||||||
|
public double? Max;
|
||||||
|
public bool IsInclusive;
|
||||||
|
|
||||||
|
public bool Equals(OptionalRange range) => Min == range.Min && Max == range.Max;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user