1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 21:27:24 +08:00

Merge branch 'master' into hud/kc-skinnable

This commit is contained in:
Bartłomiej Dach 2023-06-27 20:35:47 +02:00
commit af66ccbfdf
No known key found for this signature in database
7 changed files with 104 additions and 25 deletions

View File

@ -159,6 +159,31 @@ namespace osu.Game.Tests.NonVisual.Filtering
Assert.AreEqual(filtered, carouselItem.Filtered.Value);
}
[Test]
[TestCase("\"artist\"", false)]
[TestCase("\"arti\"", true)]
[TestCase("\"artist title author\"", true)]
[TestCase("\"artist\" \"title\" \"author\"", false)]
[TestCase("\"an artist\"", true)]
[TestCase("\"tags too\"", false)]
[TestCase("\"tags to\"", true)]
[TestCase("\"version\"", false)]
[TestCase("\"an auteur\"", true)]
[TestCase("\"\\\"", true)] // nasty case, covers properly escaping user input in underlying regex.
public void TestCriteriaMatchingExactTerms(string terms, bool filtered)
{
var exampleBeatmapInfo = getExampleBeatmap();
var criteria = new FilterCriteria
{
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
SearchText = terms
};
var carouselItem = new CarouselBeatmap(exampleBeatmapInfo);
carouselItem.Filter(criteria);
Assert.AreEqual(filtered, carouselItem.Filtered.Value);
}
[Test]
[TestCase("", false)]
[TestCase("The", false)]

View File

@ -23,6 +23,31 @@ namespace osu.Game.Tests.NonVisual.Filtering
Assert.AreEqual(4, filterCriteria.SearchTerms.Length);
}
[Test]
public void TestApplyQueriesBareWordsWithExactMatch()
{
const string query = "looking for \"a beatmap\" like \"this\"";
var filterCriteria = new FilterCriteria();
FilterQueryParser.ApplyQueries(filterCriteria, query);
Assert.AreEqual("looking for \"a beatmap\" like \"this\"", filterCriteria.SearchText);
Assert.AreEqual(5, filterCriteria.SearchTerms.Length);
Assert.That(filterCriteria.SearchTerms[0].SearchTerm, Is.EqualTo("a beatmap"));
Assert.That(filterCriteria.SearchTerms[0].Exact, Is.True);
Assert.That(filterCriteria.SearchTerms[1].SearchTerm, Is.EqualTo("this"));
Assert.That(filterCriteria.SearchTerms[1].Exact, Is.True);
Assert.That(filterCriteria.SearchTerms[2].SearchTerm, Is.EqualTo("looking"));
Assert.That(filterCriteria.SearchTerms[2].Exact, Is.False);
Assert.That(filterCriteria.SearchTerms[3].SearchTerm, Is.EqualTo("for"));
Assert.That(filterCriteria.SearchTerms[3].Exact, Is.False);
Assert.That(filterCriteria.SearchTerms[4].SearchTerm, Is.EqualTo("like"));
Assert.That(filterCriteria.SearchTerms[4].Exact, Is.False);
}
/*
* The following tests have been written a bit strangely (they don't check exact
* bound equality with what the filter says).
@ -235,6 +260,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
Assert.AreEqual("find me songs by please", filterCriteria.SearchText.Trim());
Assert.AreEqual(5, filterCriteria.SearchTerms.Length);
Assert.AreEqual("singer", filterCriteria.Artist.SearchTerm);
Assert.That(filterCriteria.Artist.Exact, Is.False);
}
[Test]
@ -246,6 +272,7 @@ namespace osu.Game.Tests.NonVisual.Filtering
Assert.AreEqual("really like yes", filterCriteria.SearchText.Trim());
Assert.AreEqual(3, filterCriteria.SearchTerms.Length);
Assert.AreEqual("name with space", filterCriteria.Artist.SearchTerm);
Assert.That(filterCriteria.Artist.Exact, Is.True);
}
[Test]

View File

@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
AddAssert("collection filter still selected", () => control.CreateCriteria().CollectionBeatmapMD5Hashes.Any());
AddAssert("collection filter still selected", () => control.CreateCriteria().CollectionBeatmapMD5Hashes?.Any() == true);
AddAssert("filter request not fired", () => !received);
}

View File

@ -378,9 +378,6 @@ namespace osu.Game.Screens.Play
IsBreakTime.BindTo(breakTracker.IsBreakTime);
IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
if (Configuration.AutomaticallySkipIntro)
skipIntroOverlay.SkipWhenReady();
loadLeaderboard();
}
@ -1086,6 +1083,9 @@ namespace osu.Game.Screens.Play
throw new InvalidOperationException($"{nameof(StartGameplay)} should not be called when the gameplay clock is already running");
GameplayClockContainer.Reset(startClock: true);
if (Configuration.AutomaticallySkipIntro)
skipIntroOverlay.SkipWhenReady();
}
public override void OnSuspending(ScreenTransitionEvent e)

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select.Filter;
@ -65,16 +64,16 @@ namespace osu.Game.Screens.Select.Carousel
if (criteria.SearchTerms.Length > 0)
{
var terms = BeatmapInfo.GetSearchableTerms();
var searchableTerms = BeatmapInfo.GetSearchableTerms();
foreach (string criteriaTerm in criteria.SearchTerms)
foreach (FilterCriteria.OptionalTextFilter criteriaTerm in criteria.SearchTerms)
{
bool any = false;
// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (string term in terms)
foreach (string searchTerm in searchableTerms)
{
if (!term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)) continue;
if (!criteriaTerm.Matches(searchTerm)) continue;
any = true;
break;
@ -98,7 +97,6 @@ namespace osu.Game.Screens.Select.Carousel
if (!match) return false;
match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true;
if (match && criteria.RulesetCriteria != null)
match &= criteria.RulesetCriteria.Matches(BeatmapInfo);

View File

@ -1,12 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using System.Text.RegularExpressions;
using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Rulesets;
@ -20,7 +18,7 @@ namespace osu.Game.Screens.Select
public GroupMode Group;
public SortMode Sort;
public BeatmapSetInfo SelectedBeatmapSet;
public BeatmapSetInfo? SelectedBeatmapSet;
public OptionalRange<double> StarDifficulty;
public OptionalRange<float> ApproachRate;
@ -40,12 +38,12 @@ namespace osu.Game.Screens.Select
IsUpperInclusive = true
};
public string[] SearchTerms = Array.Empty<string>();
public OptionalTextFilter[] SearchTerms = Array.Empty<OptionalTextFilter>();
public RulesetInfo Ruleset;
public RulesetInfo? Ruleset;
public bool AllowConvertedBeatmaps;
private string searchText;
private string searchText = string.Empty;
/// <summary>
/// <see cref="SearchText"/> as a number (if it can be parsed as one).
@ -58,11 +56,29 @@ namespace osu.Game.Screens.Select
set
{
searchText = value;
SearchTerms = searchText.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToArray();
List<OptionalTextFilter> terms = new List<OptionalTextFilter>();
string remainingText = value;
// First handle quoted segments to ensure we keep inline spaces in exact matches.
foreach (Match quotedSegment in Regex.Matches(searchText, "(\"[^\"]+\")"))
{
terms.Add(new OptionalTextFilter { SearchTerm = quotedSegment.Value });
remainingText = remainingText.Replace(quotedSegment.Value, string.Empty);
}
// Then handle the rest splitting on any spaces.
terms.AddRange(remainingText.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(s => new OptionalTextFilter
{
SearchTerm = s
}));
SearchTerms = terms.ToArray();
SearchNumber = null;
if (SearchTerms.Length == 1 && int.TryParse(SearchTerms[0], out int parsed))
if (SearchTerms.Length == 1 && int.TryParse(SearchTerms[0].SearchTerm, out int parsed))
SearchNumber = parsed;
}
}
@ -70,11 +86,9 @@ namespace osu.Game.Screens.Select
/// <summary>
/// Hashes from the <see cref="BeatmapCollection"/> to filter to.
/// </summary>
[CanBeNull]
public IEnumerable<string> CollectionBeatmapMD5Hashes { get; set; }
public IEnumerable<string>? CollectionBeatmapMD5Hashes { get; set; }
[CanBeNull]
public IRulesetFilterCriteria RulesetCriteria { get; set; }
public IRulesetFilterCriteria? RulesetCriteria { get; set; }
public struct OptionalRange<T> : IEquatable<OptionalRange<T>>
where T : struct
@ -124,6 +138,8 @@ namespace osu.Game.Screens.Select
{
public bool HasFilter => !string.IsNullOrEmpty(SearchTerm);
public bool Exact { get; private set; }
public bool Matches(string value)
{
if (!HasFilter)
@ -133,10 +149,23 @@ namespace osu.Game.Screens.Select
if (string.IsNullOrEmpty(value))
return false;
if (Exact)
return Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
return value.Contains(SearchTerm, StringComparison.InvariantCultureIgnoreCase);
}
public string SearchTerm;
private string searchTerm;
public string SearchTerm
{
get => searchTerm;
set
{
searchTerm = value.Trim('"');
Exact = searchTerm != value;
}
}
public bool Equals(OptionalTextFilter other) => SearchTerm == other.SearchTerm;
}

View File

@ -161,7 +161,7 @@ namespace osu.Game.Screens.Select
switch (op)
{
case Operator.Equal:
textFilter.SearchTerm = value.Trim('"');
textFilter.SearchTerm = value;
return true;
default: