diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs index 7f6fb97e0c..85dfdd9af9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("4 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 4); - AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = rooms.First().Name }); + AddStep("filter one room", () => container.Filter.Value = new LoungeFilterCriteria { SearchString = rooms.First().Name }); AddUntilStep("1 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 1); @@ -160,13 +160,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add rooms", () => rooms.AddRange(GenerateRooms(3, new CatchRuleset().RulesetInfo))); // Todo: What even is this case...? - AddStep("set empty filter criteria", () => container.Filter.Value = new FilterCriteria()); + AddStep("set empty filter criteria", () => container.Filter.Value = new LoungeFilterCriteria()); AddUntilStep("5 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 5); - AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }); + AddStep("filter osu! rooms", () => container.Filter.Value = new LoungeFilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }); AddUntilStep("2 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2); - AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }); + AddStep("filter catch rooms", () => container.Filter.Value = new LoungeFilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }); AddUntilStep("3 rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 3); } @@ -183,11 +183,11 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("both rooms visible", () => container.DrawableRooms.Count(r => r.IsPresent) == 2); - AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public }); + AddStep("filter public rooms", () => container.Filter.Value = new LoungeFilterCriteria { Permissions = RoomPermissionsFilter.Public }); AddUntilStep("private room hidden", () => container.DrawableRooms.All(r => !r.Room.HasPassword)); - AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private }); + AddStep("filter private rooms", () => container.Filter.Value = new LoungeFilterCriteria { Permissions = RoomPermissionsFilter.Private }); AddUntilStep("public room hidden", () => container.DrawableRooms.All(r => r.Room.HasPassword)); } diff --git a/osu.Game/Online/Rooms/GetRoomsRequest.cs b/osu.Game/Online/Rooms/GetRoomsRequest.cs index 2d0d572e84..a665cb730e 100644 --- a/osu.Game/Online/Rooms/GetRoomsRequest.cs +++ b/osu.Game/Online/Rooms/GetRoomsRequest.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.Rooms private readonly RoomStatusFilter? status; private readonly string category; - public GetRoomsRequest(FilterCriteria filterCriteria) + public GetRoomsRequest(LoungeFilterCriteria filterCriteria) { mode = filterCriteria.Mode; category = filterCriteria.Category; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/LoungeFilterCriteria.cs similarity index 93% rename from osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs rename to osu.Game/Screens/OnlinePlay/Lounge/Components/LoungeFilterCriteria.cs index 121dffde1f..9778b855fc 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/FilterCriteria.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/LoungeFilterCriteria.cs @@ -5,7 +5,7 @@ using osu.Game.Rulesets; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public class FilterCriteria + public class LoungeFilterCriteria { public string SearchString = string.Empty; public RoomModeFilter Mode; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs index f04de97f9b..78d0b6360f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components /// /// The current filter criteria. Should be managed externally. /// - public readonly Bindable Filter = new Bindable(); + public readonly Bindable Filter = new Bindable(); /// /// The currently user-selected room. @@ -87,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); } - private void applyFilterCriteria(FilterCriteria? criteria) + private void applyFilterCriteria(LoungeFilterCriteria? criteria) { roomFlow.Children.ForEach(r => { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeListingPoller.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeListingPoller.cs index d92ae7eb6e..34209cee7f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeListingPoller.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeListingPoller.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private IAPIProvider api { get; set; } = null!; public required Action RoomsReceived { get; init; } - public readonly IBindable Filter = new Bindable(); + public readonly IBindable Filter = new Bindable(); private GetRoomsRequest? lastPollRequest; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index b4b039501f..d1473bacc5 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge private IDisposable? joiningRoomOperation; - private readonly Bindable filter = new Bindable(); + private readonly Bindable filter = new Bindable(); private readonly Bindable hasListingResults = new Bindable(); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); @@ -262,7 +262,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge filter.Value = CreateFilterCriteria(); } - protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria + protected virtual LoungeFilterCriteria CreateFilterCriteria() => new LoungeFilterCriteria { SearchString = searchTextBox.Current.Value, Ruleset = ruleset.Value, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 5e2619eae3..2b61f3240d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer StatusDropdown.Current.BindValueChanged(_ => showInProgress.Alpha = StatusDropdown.Current.Value == RoomModeFilter.Open ? 1 : 0, true); } - protected override FilterCriteria CreateFilterCriteria() + protected override LoungeFilterCriteria CreateFilterCriteria() { var criteria = base.CreateFilterCriteria(); criteria.Category = @"realtime"; diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs index cc4065a82b..a095a5c0ef 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsLoungeSubScreen.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists return base.CreateFilterControls().Append(categoryDropdown); } - protected override FilterCriteria CreateFilterCriteria() + protected override LoungeFilterCriteria CreateFilterCriteria() { var criteria = base.CreateFilterCriteria(); diff --git a/osu.Game/Screens/Select/FilterControl.DifficultyRangeSlider.cs b/osu.Game/Screens/Select/FilterControl.DifficultyRangeSlider.cs index 902b335c62..718e55df13 100644 --- a/osu.Game/Screens/Select/FilterControl.DifficultyRangeSlider.cs +++ b/osu.Game/Screens/Select/FilterControl.DifficultyRangeSlider.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, OsuColour colours) + private void load(OverlayColourProvider colourProvider) { SliderContainer.AddRange(new Drawable[] { diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 32dd22da3e..c8c4343637 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -13,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Collections; using osu.Game.Configuration; @@ -244,8 +244,20 @@ namespace osu.Game.Screens.Select }); searchTextBox.Current.BindValueChanged(_ => updateCriteria()); - difficultyRangeSlider.LowerBound.BindValueChanged(_ => updateCriteria()); - difficultyRangeSlider.UpperBound.BindValueChanged(_ => updateCriteria()); + + ScheduledDelegate? sliderDebounce = null; + + // For slider dragging (where input events can arrive very often), even creating criteria can have + // overhead, especially when a collection is selected (see ToImmutableHashSet() call). + void debouncedUpdateCriteria() + { + sliderDebounce?.Cancel(); + sliderDebounce = Scheduler.AddDelayed(() => updateCriteria(), 50); + } + + difficultyRangeSlider.LowerBound.BindValueChanged(_ => debouncedUpdateCriteria()); + difficultyRangeSlider.UpperBound.BindValueChanged(_ => debouncedUpdateCriteria()); + showConvertedBeatmapsButton.Active.BindValueChanged(_ => updateCriteria()); sortDropdown.Current.BindValueChanged(_ => updateCriteria()); groupDropdown.Current.BindValueChanged(_ => updateCriteria()); @@ -258,7 +270,7 @@ namespace osu.Game.Screens.Select updateCriteria(); }); - collectionsSubscription = realm.RegisterForNotifications(r => r.All(), (collections, changeSet) => + collectionsSubscription = realm.RegisterForNotifications(r => r.All(), (_, changeSet) => { if (changeSet != null && groupDropdown.Current.Value.Value == GroupMode.Collections) updateCriteria(); @@ -293,7 +305,7 @@ namespace osu.Game.Screens.Select AllowConvertedBeatmaps = showConvertedBeatmapsButton.Active.Value, Ruleset = ruleset.Value, Mods = mods.Value, - CollectionBeatmapMD5Hashes = collectionDropdown.Current.Value?.Collection?.PerformRead(c => c.BeatmapMD5Hashes).ToImmutableHashSet(), + Collection = collectionDropdown.Current.Value?.Collection, LocalUserId = isValidUser ? localUser.Value.Id : null, LocalUserUsername = isValidUser ? localUser.Value.Username : null, }; diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 1c70132850..750938ceb8 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using osu.Game.Beatmaps; using osu.Game.Collections; +using osu.Game.Database; using osu.Game.Rulesets; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mods; @@ -111,10 +113,24 @@ namespace osu.Game.Screens.Select } } + private ImmutableHashSet? collectionBeatmapMD5Hashes; + private Live? collection; + /// /// Hashes from the to filter to. /// - public IEnumerable? CollectionBeatmapMD5Hashes { get; set; } + public IEnumerable? CollectionBeatmapMD5Hashes => + collectionBeatmapMD5Hashes ??= Collection?.PerformRead(c => c.BeatmapMD5Hashes.ToImmutableHashSet()); + + public Live? Collection + { + get => collection; + set + { + collection = value; + collectionBeatmapMD5Hashes = null; + } + } public IRulesetFilterCriteria? RulesetCriteria { get; set; }