mirror of
https://github.com/ppy/osu
synced 2024-12-29 10:22:43 +00:00
Merge branch 'master' into tournament-remove-minimum-window-size
This commit is contained in:
commit
8398e07be9
@ -9,6 +9,7 @@ using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@ -16,6 +17,7 @@ using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.GameplayTest;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -203,6 +205,33 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("wait for music stopped", () => !Game.MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
[TestCase(SortMode.Title)]
|
||||
[TestCase(SortMode.Difficulty)]
|
||||
public void TestSelectionRetainedOnExit(SortMode sortMode)
|
||||
{
|
||||
BeatmapSetInfo beatmapSet = null!;
|
||||
|
||||
AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely());
|
||||
AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach());
|
||||
|
||||
AddStep($"set sort mode to {sortMode}", () => Game.LocalConfig.SetValue(OsuSetting.SongSelectSortingMode, sortMode));
|
||||
AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet));
|
||||
AddUntilStep("wait for song select",
|
||||
() => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
|
||||
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
|
||||
&& songSelect.IsLoaded);
|
||||
|
||||
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
|
||||
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
|
||||
|
||||
AddStep("exit editor", () => InputManager.Key(Key.Escape));
|
||||
AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor);
|
||||
|
||||
AddUntilStep("selection retained on song select",
|
||||
() => Game.Beatmap.Value.BeatmapInfo.ID,
|
||||
() => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID));
|
||||
}
|
||||
|
||||
private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single();
|
||||
|
||||
private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen;
|
||||
|
@ -39,6 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
private BeatmapInfo currentSelection => carousel.SelectedBeatmapInfo;
|
||||
|
||||
private const int set_count = 5;
|
||||
private const int diff_count = 3;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
@ -111,7 +112,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestScrollPositionMaintainedOnAdd()
|
||||
{
|
||||
loadBeatmaps(count: 1, randomDifficulties: false);
|
||||
loadBeatmaps(setCount: 1);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@ -124,7 +125,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestDeletion()
|
||||
{
|
||||
loadBeatmaps(count: 5, randomDifficulties: true);
|
||||
loadBeatmaps(setCount: 5, randomDifficulties: true);
|
||||
|
||||
AddStep("remove first set", () => carousel.RemoveBeatmapSet(carousel.Items.Select(item => item.Item).OfType<CarouselBeatmapSet>().First().BeatmapSet));
|
||||
AddUntilStep("4 beatmap sets visible", () => this.ChildrenOfType<DrawableCarouselBeatmapSet>().Count(set => set.Alpha > 0) == 4);
|
||||
@ -133,7 +134,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestScrollPositionMaintainedOnDelete()
|
||||
{
|
||||
loadBeatmaps(count: 50, randomDifficulties: false);
|
||||
loadBeatmaps(setCount: 50);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@ -150,7 +151,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestManyPanels()
|
||||
{
|
||||
loadBeatmaps(count: 5000, randomDifficulties: true);
|
||||
loadBeatmaps(setCount: 5000, randomDifficulties: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -501,6 +502,33 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
waitForSelection(set_count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddRemoveDifficultySort()
|
||||
{
|
||||
const int local_set_count = 2;
|
||||
const int local_diff_count = 2;
|
||||
|
||||
loadBeatmaps(setCount: local_set_count, diffCount: local_diff_count);
|
||||
|
||||
AddStep("Sort by difficulty", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty }, false));
|
||||
|
||||
checkVisibleItemCount(false, local_set_count * local_diff_count);
|
||||
|
||||
var firstAdded = TestResources.CreateTestBeatmapSetInfo(local_diff_count);
|
||||
|
||||
AddStep("Add new set", () => carousel.UpdateBeatmapSet(firstAdded));
|
||||
|
||||
checkVisibleItemCount(false, (local_set_count + 1) * local_diff_count);
|
||||
|
||||
AddStep("Remove set", () => carousel.RemoveBeatmapSet(firstAdded));
|
||||
|
||||
checkVisibleItemCount(false, (local_set_count) * local_diff_count);
|
||||
|
||||
setSelected(local_set_count, 1);
|
||||
|
||||
waitForSelection(local_set_count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectionEnteringFromEmptyRuleset()
|
||||
{
|
||||
@ -662,7 +690,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
@ -709,7 +737,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
@ -758,32 +786,54 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSortingWithFiltered()
|
||||
public void TestSortingWithDifficultyFiltered()
|
||||
{
|
||||
const int local_diff_count = 3;
|
||||
const int local_set_count = 2;
|
||||
|
||||
List<BeatmapSetInfo> sets = new List<BeatmapSetInfo>();
|
||||
|
||||
AddStep("Populuate beatmap sets", () =>
|
||||
{
|
||||
sets.Clear();
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int i = 0; i < local_set_count; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(local_diff_count);
|
||||
set.Beatmaps[0].StarRating = 3 - i;
|
||||
set.Beatmaps[2].StarRating = 6 + i;
|
||||
set.Beatmaps[1].StarRating = 6 + i;
|
||||
sets.Add(set);
|
||||
}
|
||||
});
|
||||
|
||||
loadBeatmaps(sets);
|
||||
|
||||
AddStep("Sort by difficulty", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty }, false));
|
||||
|
||||
checkVisibleItemCount(false, local_set_count * local_diff_count);
|
||||
checkVisibleItemCount(true, 1);
|
||||
|
||||
AddStep("Filter to normal", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty, SearchText = "Normal" }, false));
|
||||
AddAssert("Check first set at end", () => carousel.BeatmapSets.First().Equals(sets.Last()));
|
||||
AddAssert("Check last set at start", () => carousel.BeatmapSets.Last().Equals(sets.First()));
|
||||
checkVisibleItemCount(false, local_set_count);
|
||||
checkVisibleItemCount(true, 1);
|
||||
|
||||
AddUntilStep("Check all visible sets have one normal", () =>
|
||||
{
|
||||
return carousel.Items.OfType<DrawableCarouselBeatmapSet>()
|
||||
.Where(p => p.IsPresent)
|
||||
.Count(p => ((CarouselBeatmapSet)p.Item)!.Beatmaps.Single().BeatmapInfo.DifficultyName.StartsWith("Normal", StringComparison.Ordinal)) == local_set_count;
|
||||
});
|
||||
|
||||
AddStep("Filter to insane", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty, SearchText = "Insane" }, false));
|
||||
AddAssert("Check first set at start", () => carousel.BeatmapSets.First().Equals(sets.First()));
|
||||
AddAssert("Check last set at end", () => carousel.BeatmapSets.Last().Equals(sets.Last()));
|
||||
checkVisibleItemCount(false, local_set_count);
|
||||
checkVisibleItemCount(true, 1);
|
||||
|
||||
AddUntilStep("Check all visible sets have one insane", () =>
|
||||
{
|
||||
return carousel.Items.OfType<DrawableCarouselBeatmapSet>()
|
||||
.Where(p => p.IsPresent)
|
||||
.Count(p => ((CarouselBeatmapSet)p.Item)!.Beatmaps.Single().BeatmapInfo.DifficultyName.StartsWith("Insane", StringComparison.Ordinal)) == local_set_count;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -838,7 +888,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddStep("create hidden set", () =>
|
||||
{
|
||||
hidingSet = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
hidingSet = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
hidingSet.Beatmaps[1].Hidden = true;
|
||||
|
||||
hiddenList.Clear();
|
||||
@ -885,7 +935,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddStep("add mixed ruleset beatmapset", () =>
|
||||
{
|
||||
testMixed = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
testMixed = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
for (int i = 0; i <= 2; i++)
|
||||
{
|
||||
@ -907,7 +957,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
BeatmapSetInfo testSingle = null;
|
||||
AddStep("add single ruleset beatmapset", () =>
|
||||
{
|
||||
testSingle = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
testSingle = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
testSingle.Beatmaps.ForEach(b =>
|
||||
{
|
||||
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
|
||||
@ -930,7 +980,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
manySets.Clear();
|
||||
|
||||
for (int i = 1; i <= 50; i++)
|
||||
manySets.Add(TestResources.CreateTestBeatmapSetInfo(3));
|
||||
manySets.Add(TestResources.CreateTestBeatmapSetInfo(diff_count));
|
||||
});
|
||||
|
||||
loadBeatmaps(manySets);
|
||||
@ -955,6 +1005,43 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCarouselRemembersSelectionDifficultySort()
|
||||
{
|
||||
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
|
||||
|
||||
AddStep("Populate beatmap sets", () =>
|
||||
{
|
||||
manySets.Clear();
|
||||
|
||||
for (int i = 1; i <= 50; i++)
|
||||
manySets.Add(TestResources.CreateTestBeatmapSetInfo(diff_count));
|
||||
});
|
||||
|
||||
loadBeatmaps(manySets);
|
||||
|
||||
AddStep("Sort by difficulty", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty }, false));
|
||||
|
||||
advanceSelection(direction: 1, diff: false);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
AddStep("Toggle non-matching filter", () =>
|
||||
{
|
||||
carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
|
||||
});
|
||||
|
||||
AddStep("Restore no filter", () =>
|
||||
{
|
||||
carousel.Filter(new FilterCriteria(), false);
|
||||
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet!.ID);
|
||||
});
|
||||
}
|
||||
|
||||
// always returns to same selection as long as it's available.
|
||||
AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFilteringByUserStarDifficulty()
|
||||
{
|
||||
@ -1081,8 +1168,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null, int? count = null,
|
||||
bool randomDifficulties = false)
|
||||
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null,
|
||||
int? setCount = null, int? diffCount = null, bool randomDifficulties = false)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
@ -1090,11 +1177,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
for (int i = 1; i <= (count ?? set_count); i++)
|
||||
for (int i = 1; i <= (setCount ?? set_count); i++)
|
||||
{
|
||||
beatmapSets.Add(randomDifficulties
|
||||
? TestResources.CreateTestBeatmapSetInfo()
|
||||
: TestResources.CreateTestBeatmapSetInfo(3));
|
||||
: TestResources.CreateTestBeatmapSetInfo(diffCount ?? diff_count));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using osu.Game.Tests.Online;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Input;
|
||||
@ -192,6 +193,57 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep("release mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSplitDisplay()
|
||||
{
|
||||
ArchiveDownloadRequest<IBeatmapSetInfo>? downloadRequest = null;
|
||||
|
||||
AddStep("set difficulty sort mode", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty }));
|
||||
AddStep("update online hash", () =>
|
||||
{
|
||||
testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash";
|
||||
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
|
||||
|
||||
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
|
||||
});
|
||||
|
||||
AddUntilStep("multiple \"sets\" visible", () => carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().Count(), () => Is.GreaterThan(1));
|
||||
AddUntilStep("update button visible", getUpdateButton, () => Is.Not.Null);
|
||||
|
||||
AddStep("click button", () => getUpdateButton()?.TriggerClick());
|
||||
|
||||
AddUntilStep("wait for download started", () =>
|
||||
{
|
||||
downloadRequest = beatmapDownloader.GetExistingDownload(testBeatmapSetInfo);
|
||||
return downloadRequest != null;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for button disabled", () => getUpdateButton()?.Enabled.Value == false);
|
||||
|
||||
AddUntilStep("progress download to completion", () =>
|
||||
{
|
||||
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||
{
|
||||
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||
|
||||
if (testRequest.Progress >= 1)
|
||||
{
|
||||
testRequest.TriggerSuccess();
|
||||
|
||||
// usually this would be done by the import process.
|
||||
testBeatmapSetInfo.Beatmaps.First().MD5Hash = "different hash";
|
||||
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
|
||||
|
||||
// usually this would be done by a realm subscription.
|
||||
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private BeatmapCarousel createCarousel()
|
||||
{
|
||||
return carousel = new BeatmapCarousel
|
||||
@ -199,7 +251,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BeatmapSets = new List<BeatmapSetInfo>
|
||||
{
|
||||
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo()),
|
||||
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(5)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -7,13 +7,16 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament
|
||||
{
|
||||
internal partial class SaveChangesOverlay : CompositeDrawable
|
||||
internal partial class SaveChangesOverlay : CompositeDrawable, IKeyBindingHandler<PlatformAction>
|
||||
{
|
||||
[Resolved]
|
||||
private TournamentGame tournamentGame { get; set; } = null!;
|
||||
@ -78,6 +81,21 @@ namespace osu.Game.Tournament
|
||||
scheduleNextCheck();
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
if (e.Action == PlatformAction.Save && !e.Repeat)
|
||||
{
|
||||
saveChangesButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
private void scheduleNextCheck() => Scheduler.AddDelayed(() => checkForChanges().FireAndForget(), 1000);
|
||||
|
||||
private void saveChanges()
|
||||
|
@ -21,8 +21,9 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
User = new APIUser
|
||||
{
|
||||
Id = APIUser.SYSTEM_USER_ID,
|
||||
Id = replayData.User.OnlineID,
|
||||
Username = replayData.User.Username,
|
||||
IsBot = replayData.User.IsBot,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -156,7 +156,7 @@ namespace osu.Game.Screens.Ranking
|
||||
if (Score != null)
|
||||
{
|
||||
// only show flair / animation when arriving after watching a play that isn't autoplay.
|
||||
bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable);
|
||||
bool shouldFlair = player != null && !Score.User.IsBot;
|
||||
|
||||
ScorePanelList.AddScore(Score, shouldFlair);
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private CarouselBeatmapSet? selectedBeatmapSet;
|
||||
|
||||
private List<BeatmapSetInfo> originalBeatmapSetsDetached = new List<BeatmapSetInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the <see cref="SelectedBeatmapInfo"/> is changed.
|
||||
/// </summary>
|
||||
@ -127,15 +129,37 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void loadBeatmapSets(IEnumerable<BeatmapSetInfo> beatmapSets)
|
||||
{
|
||||
originalBeatmapSetsDetached = beatmapSets.Detach();
|
||||
|
||||
if (selectedBeatmapSet != null && !originalBeatmapSetsDetached.Contains(selectedBeatmapSet.BeatmapSet))
|
||||
selectedBeatmapSet = null;
|
||||
|
||||
var selectedBeatmapBefore = selectedBeatmap?.BeatmapInfo;
|
||||
|
||||
CarouselRoot newRoot = new CarouselRoot(this);
|
||||
|
||||
newRoot.AddItems(beatmapSets.Select(s => createCarouselSet(s.Detach())).OfType<CarouselBeatmapSet>());
|
||||
if (beatmapsSplitOut)
|
||||
{
|
||||
var carouselBeatmapSets = originalBeatmapSetsDetached.SelectMany(s => s.Beatmaps).Select(b =>
|
||||
{
|
||||
return createCarouselSet(new BeatmapSetInfo(new[] { b })
|
||||
{
|
||||
ID = b.BeatmapSet!.ID,
|
||||
OnlineID = b.BeatmapSet!.OnlineID
|
||||
});
|
||||
}).OfType<CarouselBeatmapSet>();
|
||||
|
||||
newRoot.AddItems(carouselBeatmapSets);
|
||||
}
|
||||
else
|
||||
{
|
||||
var carouselBeatmapSets = originalBeatmapSetsDetached.Select(createCarouselSet).OfType<CarouselBeatmapSet>();
|
||||
|
||||
newRoot.AddItems(carouselBeatmapSets);
|
||||
}
|
||||
|
||||
root = newRoot;
|
||||
|
||||
if (selectedBeatmapSet != null && !beatmapSets.Contains(selectedBeatmapSet.BeatmapSet))
|
||||
selectedBeatmapSet = null;
|
||||
|
||||
Scroll.Clear(false);
|
||||
itemsCache.Invalidate();
|
||||
ScrollToSelected();
|
||||
@ -144,6 +168,15 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (loadedTestBeatmaps)
|
||||
signalBeatmapsLoaded();
|
||||
|
||||
// Restore selection
|
||||
if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates))
|
||||
{
|
||||
CarouselBeatmap? found = newSelectionCandidates.SelectMany(s => s.Beatmaps).SingleOrDefault(b => b.BeatmapInfo.ID == selectedBeatmapBefore.ID);
|
||||
|
||||
if (found != null)
|
||||
found.State.Value = CarouselItemState.Selected;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<CarouselItem> visibleItems = new List<CarouselItem>();
|
||||
@ -330,8 +363,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// Only require to action here if the beatmap is missing.
|
||||
// This avoids processing these events unnecessarily when new beatmaps are imported, for example.
|
||||
if (root.BeatmapSetsByID.TryGetValue(beatmapSet.ID, out var existingSet)
|
||||
&& existingSet.BeatmapSet.Beatmaps.All(b => b.ID != beatmapInfo.ID))
|
||||
if (root.BeatmapSetsByID.TryGetValue(beatmapSet.ID, out var existingSets)
|
||||
&& existingSets.SelectMany(s => s.Beatmaps).All(b => b.BeatmapInfo.ID != beatmapInfo.ID))
|
||||
{
|
||||
UpdateBeatmapSet(beatmapSet.Detach());
|
||||
}
|
||||
@ -345,15 +378,20 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void removeBeatmapSet(Guid beatmapSetID) => Schedule(() =>
|
||||
{
|
||||
if (!root.BeatmapSetsByID.TryGetValue(beatmapSetID, out var existingSet))
|
||||
if (!root.BeatmapSetsByID.TryGetValue(beatmapSetID, out var existingSets))
|
||||
return;
|
||||
|
||||
foreach (var beatmap in existingSet.Beatmaps)
|
||||
randomSelectedBeatmaps.Remove(beatmap);
|
||||
originalBeatmapSetsDetached.RemoveAll(set => set.ID == beatmapSetID);
|
||||
|
||||
previouslyVisitedRandomSets.Remove(existingSet);
|
||||
foreach (var set in existingSets)
|
||||
{
|
||||
foreach (var beatmap in set.Beatmaps)
|
||||
randomSelectedBeatmaps.Remove(beatmap);
|
||||
previouslyVisitedRandomSets.Remove(set);
|
||||
|
||||
root.RemoveItem(set);
|
||||
}
|
||||
|
||||
root.RemoveItem(existingSet);
|
||||
itemsCache.Invalidate();
|
||||
|
||||
if (!Scroll.UserScrolling)
|
||||
@ -366,26 +404,63 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
Guid? previouslySelectedID = null;
|
||||
|
||||
originalBeatmapSetsDetached.RemoveAll(set => set.ID == beatmapSet.ID);
|
||||
originalBeatmapSetsDetached.Add(beatmapSet.Detach());
|
||||
|
||||
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
|
||||
if (selectedBeatmapSet?.BeatmapSet.ID == beatmapSet.ID)
|
||||
previouslySelectedID = selectedBeatmap?.BeatmapInfo.ID;
|
||||
|
||||
var newSet = createCarouselSet(beatmapSet);
|
||||
var removedSet = root.RemoveChild(beatmapSet.ID);
|
||||
var removedSets = root.RemoveItemsByID(beatmapSet.ID);
|
||||
|
||||
// If we don't remove this here, it may remain in a hidden state until scrolled off screen.
|
||||
// Doesn't really affect anything during actual user interaction, but makes testing annoying.
|
||||
var removedDrawable = Scroll.FirstOrDefault(c => c.Item == removedSet);
|
||||
if (removedDrawable != null)
|
||||
expirePanelImmediately(removedDrawable);
|
||||
|
||||
if (newSet != null)
|
||||
foreach (var removedSet in removedSets)
|
||||
{
|
||||
root.AddItem(newSet);
|
||||
// If we don't remove this here, it may remain in a hidden state until scrolled off screen.
|
||||
// Doesn't really affect anything during actual user interaction, but makes testing annoying.
|
||||
var removedDrawable = Scroll.FirstOrDefault(c => c.Item == removedSet);
|
||||
if (removedDrawable != null)
|
||||
expirePanelImmediately(removedDrawable);
|
||||
}
|
||||
|
||||
if (beatmapsSplitOut)
|
||||
{
|
||||
var newSets = new List<CarouselBeatmapSet>();
|
||||
|
||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||
{
|
||||
var newSet = createCarouselSet(new BeatmapSetInfo(new[] { beatmap })
|
||||
{
|
||||
ID = beatmapSet.ID,
|
||||
OnlineID = beatmapSet.OnlineID
|
||||
});
|
||||
|
||||
if (newSet != null)
|
||||
{
|
||||
newSets.Add(newSet);
|
||||
root.AddItem(newSet);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we can/need to maintain our current selection.
|
||||
if (previouslySelectedID != null)
|
||||
select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
||||
{
|
||||
var toSelect = newSets.FirstOrDefault(s => s.Beatmaps.Any(b => b.BeatmapInfo.ID == previouslySelectedID))
|
||||
?? newSets.FirstOrDefault();
|
||||
select(toSelect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var newSet = createCarouselSet(beatmapSet);
|
||||
|
||||
if (newSet != null)
|
||||
{
|
||||
root.AddItem(newSet);
|
||||
|
||||
// check if we can/need to maintain our current selection.
|
||||
if (previouslySelectedID != null)
|
||||
select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
||||
}
|
||||
}
|
||||
|
||||
itemsCache.Invalidate();
|
||||
@ -632,6 +707,8 @@ namespace osu.Game.Screens.Select
|
||||
applyActiveCriteria(debounce);
|
||||
}
|
||||
|
||||
private bool beatmapsSplitOut;
|
||||
|
||||
private void applyActiveCriteria(bool debounce, bool alwaysResetScrollPosition = true)
|
||||
{
|
||||
PendingFilter?.Cancel();
|
||||
@ -652,6 +729,13 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
PendingFilter = null;
|
||||
|
||||
if (activeCriteria.SplitOutDifficulties != beatmapsSplitOut)
|
||||
{
|
||||
beatmapsSplitOut = activeCriteria.SplitOutDifficulties;
|
||||
loadBeatmapSets(originalBeatmapSetsDetached);
|
||||
return;
|
||||
}
|
||||
|
||||
root.Filter(activeCriteria);
|
||||
itemsCache.Invalidate();
|
||||
|
||||
@ -1055,7 +1139,7 @@ namespace osu.Game.Screens.Select
|
||||
// May only be null during construction (State.Value set causes PerformSelection to be triggered).
|
||||
private readonly BeatmapCarousel? carousel;
|
||||
|
||||
public readonly Dictionary<Guid, CarouselBeatmapSet> BeatmapSetsByID = new Dictionary<Guid, CarouselBeatmapSet>();
|
||||
public readonly Dictionary<Guid, List<CarouselBeatmapSet>> BeatmapSetsByID = new Dictionary<Guid, List<CarouselBeatmapSet>>();
|
||||
|
||||
public CarouselRoot(BeatmapCarousel carousel)
|
||||
{
|
||||
@ -1069,20 +1153,25 @@ namespace osu.Game.Screens.Select
|
||||
public override void AddItem(CarouselItem i)
|
||||
{
|
||||
CarouselBeatmapSet set = (CarouselBeatmapSet)i;
|
||||
BeatmapSetsByID.Add(set.BeatmapSet.ID, set);
|
||||
if (BeatmapSetsByID.TryGetValue(set.BeatmapSet.ID, out var sets))
|
||||
sets.Add(set);
|
||||
else
|
||||
BeatmapSetsByID.Add(set.BeatmapSet.ID, new List<CarouselBeatmapSet> { set });
|
||||
|
||||
base.AddItem(i);
|
||||
}
|
||||
|
||||
public CarouselBeatmapSet? RemoveChild(Guid beatmapSetID)
|
||||
public IEnumerable<CarouselBeatmapSet> RemoveItemsByID(Guid beatmapSetID)
|
||||
{
|
||||
if (BeatmapSetsByID.TryGetValue(beatmapSetID, out var carouselBeatmapSet))
|
||||
if (BeatmapSetsByID.TryGetValue(beatmapSetID, out var carouselBeatmapSets))
|
||||
{
|
||||
RemoveItem(carouselBeatmapSet);
|
||||
return carouselBeatmapSet;
|
||||
foreach (var set in carouselBeatmapSets)
|
||||
RemoveItem(set);
|
||||
|
||||
return carouselBeatmapSets;
|
||||
}
|
||||
|
||||
return null;
|
||||
return Enumerable.Empty<CarouselBeatmapSet>();
|
||||
}
|
||||
|
||||
public override void RemoveItem(CarouselItem i)
|
||||
|
@ -19,6 +19,11 @@ namespace osu.Game.Screens.Select
|
||||
public GroupMode Group;
|
||||
public SortMode Sort;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the display of beatmap sets should be split apart per-difficulty for the current criteria.
|
||||
/// </summary>
|
||||
public bool SplitOutDifficulties => Sort == SortMode.Difficulty;
|
||||
|
||||
public BeatmapSetInfo? SelectedBeatmapSet;
|
||||
|
||||
public OptionalRange<double> StarDifficulty;
|
||||
|
Loading…
Reference in New Issue
Block a user