1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 23:05:37 +08:00

Merge pull request #25949 from peppy/fix-song-select-extra-selections

Fix beatmap updates causing one extra carousel selection
This commit is contained in:
Bartłomiej Dach 2023-12-20 15:30:38 +01:00 committed by GitHub
commit 77d8bbf152
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 39 deletions

View File

@ -455,30 +455,13 @@ namespace osu.Game.Screens.Select
private void updateBeatmapSet(BeatmapSetInfo beatmapSet)
{
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 removedSets = root.RemoveItemsByID(beatmapSet.ID);
foreach (var removedSet in removedSets)
{
// 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);
}
var newSets = new List<CarouselBeatmapSet>();
if (beatmapsSplitOut)
{
var newSets = new List<CarouselBeatmapSet>();
foreach (var beatmap in beatmapSet.Beatmaps)
{
var newSet = createCarouselSet(new BeatmapSetInfo(new[] { beatmap })
@ -489,18 +472,7 @@ namespace osu.Game.Screens.Select
});
if (newSet != null)
{
newSets.Add(newSet);
root.AddItem(newSet);
}
}
// check if we can/need to maintain our current selection.
if (previouslySelectedID != null)
{
var toSelect = newSets.FirstOrDefault(s => s.Beatmaps.Any(b => b.BeatmapInfo.ID == previouslySelectedID))
?? newSets.FirstOrDefault();
select(toSelect);
}
}
else
@ -508,13 +480,18 @@ namespace osu.Game.Screens.Select
var newSet = createCarouselSet(beatmapSet);
if (newSet != null)
{
root.AddItem(newSet);
newSets.Add(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 removedSets = root.ReplaceItem(beatmapSet, newSets);
// If we don't remove these 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.
foreach (var removedSet in removedSets)
{
var removedDrawable = Scroll.FirstOrDefault(c => c.Item == removedSet);
if (removedDrawable != null)
expirePanelImmediately(removedDrawable);
}
}
@ -1207,6 +1184,43 @@ namespace osu.Game.Screens.Select
base.AddItem(i);
}
/// <summary>
/// A special method to handle replace operations (general for updating a beatmap).
/// Avoids event-driven selection flip-flopping during the remove/add process.
/// </summary>
/// <param name="oldItem">The beatmap set to be replaced.</param>
/// <param name="newItems">All new items to replace the removed beatmap set.</param>
/// <returns>All removed items, for any further processing.</returns>
public IEnumerable<CarouselBeatmapSet> ReplaceItem(BeatmapSetInfo oldItem, List<CarouselBeatmapSet> newItems)
{
var previousSelection = (LastSelected as CarouselBeatmapSet)?.Beatmaps
.FirstOrDefault(s => s.State.Value == CarouselItemState.Selected)
?.BeatmapInfo;
bool wasSelected = previousSelection?.BeatmapSet?.ID == oldItem.ID;
// Without doing this, the removal of the old beatmap will cause carousel's eager selection
// logic to invoke, causing one unnecessary selection.
DisableSelection = true;
var removedSets = RemoveItemsByID(oldItem.ID);
DisableSelection = false;
foreach (var set in newItems)
AddItem(set);
// Check if we can/need to maintain our current selection.
if (wasSelected)
{
CarouselBeatmap? matchingBeatmap = newItems.SelectMany(s => s.Beatmaps)
.FirstOrDefault(b => b.BeatmapInfo.ID == previousSelection?.ID);
if (matchingBeatmap != null)
matchingBeatmap.State.Value = CarouselItemState.Selected;
}
return removedSets;
}
public IEnumerable<CarouselBeatmapSet> RemoveItemsByID(Guid beatmapSetID)
{
if (BeatmapSetsByID.TryGetValue(beatmapSetID, out var carouselBeatmapSets))

View File

@ -36,13 +36,13 @@ namespace osu.Game.Screens.Select.Carousel
/// items have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/>
/// operation.
/// </summary>
private bool filteringItems;
protected bool DisableSelection;
public override void Filter(FilterCriteria criteria)
{
filteringItems = true;
DisableSelection = true;
base.Filter(criteria);
filteringItems = false;
DisableSelection = false;
attemptSelection();
}
@ -95,7 +95,7 @@ namespace osu.Game.Screens.Select.Carousel
private void attemptSelection()
{
if (filteringItems) return;
if (DisableSelection) return;
// we only perform eager selection if we are a currently selected group.
if (State.Value != CarouselItemState.Selected) return;