1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 07:22:54 +08:00

Merge pull request #27881 from smoogipoo/fix-mod-refilter

Don't re-filter unless mods may change the filter
This commit is contained in:
Bartłomiej Dach 2024-04-16 11:21:35 +02:00 committed by GitHub
commit d2a67d2d54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 104 additions and 6 deletions

View File

@ -1,9 +1,14 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
@ -30,5 +35,20 @@ namespace osu.Game.Rulesets.Mania
return false;
}
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods)
{
if (keys.HasFilter)
{
// Interpreting as the Mod type is required for equality comparison.
HashSet<Mod> oldSet = mods.OldValue.OfType<ManiaKeyMod>().AsEnumerable<Mod>().ToHashSet();
HashSet<Mod> newSet = mods.NewValue.OfType<ManiaKeyMod>().AsEnumerable<Mod>().ToHashSet();
if (!oldSet.SetEquals(newSet))
return true;
}
return false;
}
}
}

View File

@ -1,10 +1,13 @@
// 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.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@ -311,6 +314,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria) => match;
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false;
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
}
}
}

View File

@ -2,10 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Filter;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@ -514,6 +517,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
return false;
}
public bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods) => false;
}
private static readonly object[] correct_date_query_examples =

View File

@ -29,6 +29,7 @@ using osu.Game.Overlays.Dialog;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
@ -1147,6 +1148,62 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("filter text cleared", () => songSelect!.FilterControl.ChildrenOfType<FilterControl.FilterControlTextBox>().First().Text, () => Is.Empty);
}
[Test]
public void TestNonFilterableModChange()
{
addRulesetImportStep(0);
createSongSelect();
// Mod that is guaranteed to never re-filter.
AddStep("add non-filterable mod", () => SelectedMods.Value = new Mod[] { new OsuModCinema() });
AddAssert("filter count is 1", () => songSelect!.FilterCount, () => Is.EqualTo(1));
// Removing the mod should still not re-filter.
AddStep("remove non-filterable mod", () => SelectedMods.Value = Array.Empty<Mod>());
AddAssert("filter count is 1", () => songSelect!.FilterCount, () => Is.EqualTo(1));
}
[Test]
public void TestFilterableModChange()
{
addRulesetImportStep(3);
createSongSelect();
// Change to mania ruleset.
AddStep("filter to mania ruleset", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.OnlineID == 3));
AddAssert("filter count is 2", () => songSelect!.FilterCount, () => Is.EqualTo(2));
// Apply a mod, but this should NOT re-filter because there's no search text.
AddStep("add filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey3() });
AddAssert("filter count is 2", () => songSelect!.FilterCount, () => Is.EqualTo(2));
// Set search text. Should re-filter.
AddStep("set search text to match mods", () => songSelect!.FilterControl.CurrentTextSearch.Value = "keys=3");
AddAssert("filter count is 3", () => songSelect!.FilterCount, () => Is.EqualTo(3));
// Change filterable mod. Should re-filter.
AddStep("change new filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey5() });
AddAssert("filter count is 4", () => songSelect!.FilterCount, () => Is.EqualTo(4));
// Add non-filterable mod. Should NOT re-filter.
AddStep("apply non-filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModNoFail(), new ManiaModKey5() });
AddAssert("filter count is 4", () => songSelect!.FilterCount, () => Is.EqualTo(4));
// Remove filterable mod. Should re-filter.
AddStep("remove filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModNoFail() });
AddAssert("filter count is 5", () => songSelect!.FilterCount, () => Is.EqualTo(5));
// Remove non-filterable mod. Should NOT re-filter.
AddStep("remove filterable mod", () => SelectedMods.Value = Array.Empty<Mod>());
AddAssert("filter count is 5", () => songSelect!.FilterCount, () => Is.EqualTo(5));
// Add filterable mod. Should re-filter.
AddStep("add filterable mod", () => SelectedMods.Value = new Mod[] { new ManiaModKey3() });
AddAssert("filter count is 6", () => songSelect!.FilterCount, () => Is.EqualTo(6));
}
private void waitForInitialSelection()
{
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);

View File

@ -1,7 +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.
using System.Collections.Generic;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
@ -52,5 +55,12 @@ namespace osu.Game.Rulesets.Filter
/// while ignored criteria are included in <see cref="FilterCriteria.SearchText"/>.
/// </returns>
bool TryParseCustomKeywordCriteria(string key, Operator op, string value);
/// <summary>
/// Whether to reapply the filter as a result of the given change in applied mods.
/// </summary>
/// <param name="mods">The change in mods.</param>
/// <returns>Whether the filter should be re-applied.</returns>
bool FilterMayChangeFromMods(ValueChangedEvent<IReadOnlyList<Mod>> mods);
}
}

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -49,15 +50,14 @@ namespace osu.Game.Screens.Select
}
private OsuTabControl<SortMode> sortTabs;
private Bindable<SortMode> sortMode;
private Bindable<GroupMode> groupMode;
private FilterControlTextBox searchTextBox;
private CollectionDropdown collectionDropdown;
[CanBeNull]
private FilterCriteria currentCriteria;
public FilterCriteria CreateCriteria()
{
string query = searchTextBox.Text;
@ -228,7 +228,8 @@ namespace osu.Game.Screens.Select
if (m.NewValue.SequenceEqual(m.OldValue))
return;
updateCriteria();
if (currentCriteria?.RulesetCriteria?.FilterMayChangeFromMods(m) == true)
updateCriteria();
});
groupMode.BindValueChanged(_ => updateCriteria());
@ -263,7 +264,7 @@ namespace osu.Game.Screens.Select
private readonly Bindable<double> minimumStars = new BindableDouble();
private readonly Bindable<double> maximumStars = new BindableDouble();
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
private void updateCriteria() => FilterChanged?.Invoke(currentCriteria = CreateCriteria());
protected override bool OnClick(ClickEvent e) => true;