mirror of
https://github.com/ppy/osu.git
synced 2026-06-08 07:43:42 +08:00
Compare commits
23 Commits
pp-dev
...
2023.1218.1
@@ -464,7 +464,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
manager.Import(testBeatmapSetInfo);
|
manager.Import(testBeatmapSetInfo);
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID);
|
AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh()));
|
||||||
|
|
||||||
|
AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID));
|
||||||
|
|
||||||
Task<Live<BeatmapSetInfo>?> updateTask = null!;
|
Task<Live<BeatmapSetInfo>?> updateTask = null!;
|
||||||
|
|
||||||
@@ -476,7 +478,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
});
|
});
|
||||||
AddUntilStep("wait for update completion", () => updateTask.IsCompleted);
|
AddUntilStep("wait for update completion", () => updateTask.IsCompleted);
|
||||||
|
|
||||||
AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID);
|
AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh()));
|
||||||
|
|
||||||
|
AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
checkForOutdatedStarRatings();
|
checkForOutdatedStarRatings();
|
||||||
processBeatmapSetsWithMissingMetrics();
|
processBeatmapSetsWithMissingMetrics();
|
||||||
|
// Note that the previous method will also update these on a fresh run.
|
||||||
processBeatmapsWithMissingObjectCounts();
|
processBeatmapsWithMissingObjectCounts();
|
||||||
processScoresWithMissingStatistics();
|
processScoresWithMissingStatistics();
|
||||||
convertLegacyTotalScoreToStandardised();
|
convertLegacyTotalScoreToStandardised();
|
||||||
@@ -144,12 +145,24 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (beatmapSetIds.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Logger.Log($"Found {beatmapSetIds.Count} beatmap sets which require reprocessing.");
|
Logger.Log($"Found {beatmapSetIds.Count} beatmap sets which require reprocessing.");
|
||||||
|
|
||||||
int i = 0;
|
// Technically this is doing more than just star ratings, but easier for the end user to understand.
|
||||||
|
var notification = showProgressNotification(beatmapSetIds.Count, "Reprocessing star rating for beatmaps", "beatmaps' star ratings have been updated");
|
||||||
|
|
||||||
|
int processedCount = 0;
|
||||||
|
int failedCount = 0;
|
||||||
|
|
||||||
foreach (var id in beatmapSetIds)
|
foreach (var id in beatmapSetIds)
|
||||||
{
|
{
|
||||||
|
if (notification?.State == ProgressNotificationState.Cancelled)
|
||||||
|
break;
|
||||||
|
|
||||||
|
updateNotificationProgress(notification, processedCount, beatmapSetIds.Count);
|
||||||
|
|
||||||
sleepIfRequired();
|
sleepIfRequired();
|
||||||
|
|
||||||
realmAccess.Run(r =>
|
realmAccess.Run(r =>
|
||||||
@@ -160,16 +173,19 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Log($"Background processing {set} ({++i} / {beatmapSetIds.Count})");
|
|
||||||
beatmapUpdater.Process(set);
|
beatmapUpdater.Process(set);
|
||||||
|
++processedCount;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Log($"Background processing failed on {set}: {e}");
|
Logger.Log($"Background processing failed on {set}: {e}");
|
||||||
|
++failedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completeNotification(notification, processedCount, beatmapSetIds.Count, failedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processBeatmapsWithMissingObjectCounts()
|
private void processBeatmapsWithMissingObjectCounts()
|
||||||
@@ -184,12 +200,23 @@ namespace osu.Game
|
|||||||
beatmapIds.Add(b.ID);
|
beatmapIds.Add(b.ID);
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing.");
|
if (beatmapIds.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
int i = 0;
|
Logger.Log($"Found {beatmapIds.Count} beatmaps which require statistics population.");
|
||||||
|
|
||||||
|
var notification = showProgressNotification(beatmapIds.Count, "Populating missing statistics for beatmaps", "beatmaps have been populated with missing statistics");
|
||||||
|
|
||||||
|
int processedCount = 0;
|
||||||
|
int failedCount = 0;
|
||||||
|
|
||||||
foreach (var id in beatmapIds)
|
foreach (var id in beatmapIds)
|
||||||
{
|
{
|
||||||
|
if (notification?.State == ProgressNotificationState.Cancelled)
|
||||||
|
break;
|
||||||
|
|
||||||
|
updateNotificationProgress(notification, processedCount, beatmapIds.Count);
|
||||||
|
|
||||||
sleepIfRequired();
|
sleepIfRequired();
|
||||||
|
|
||||||
realmAccess.Run(r =>
|
realmAccess.Run(r =>
|
||||||
@@ -200,16 +227,19 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Log($"Background processing {beatmap} ({++i} / {beatmapIds.Count})");
|
|
||||||
beatmapUpdater.ProcessObjectCounts(beatmap);
|
beatmapUpdater.ProcessObjectCounts(beatmap);
|
||||||
|
++processedCount;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Log($"Background processing failed on {beatmap}: {e}");
|
Logger.Log($"Background processing failed on {beatmap}: {e}");
|
||||||
|
++failedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completeNotification(notification, processedCount, beatmapIds.Count, failedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processScoresWithMissingStatistics()
|
private void processScoresWithMissingStatistics()
|
||||||
@@ -231,10 +261,23 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Log($"Found {scoreIds.Count} scores which require reprocessing.");
|
if (scoreIds.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Logger.Log($"Found {scoreIds.Count} scores which require statistics population.");
|
||||||
|
|
||||||
|
var notification = showProgressNotification(scoreIds.Count, "Populating missing statistics for scores", "scores have been populated with missing statistics");
|
||||||
|
|
||||||
|
int processedCount = 0;
|
||||||
|
int failedCount = 0;
|
||||||
|
|
||||||
foreach (var id in scoreIds)
|
foreach (var id in scoreIds)
|
||||||
{
|
{
|
||||||
|
if (notification?.State == ProgressNotificationState.Cancelled)
|
||||||
|
break;
|
||||||
|
|
||||||
|
updateNotificationProgress(notification, processedCount, scoreIds.Count);
|
||||||
|
|
||||||
sleepIfRequired();
|
sleepIfRequired();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -250,7 +293,7 @@ namespace osu.Game
|
|||||||
r.Find<ScoreInfo>(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics);
|
r.Find<ScoreInfo>(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics);
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Log($"Populated maximum statistics for score {id}");
|
++processedCount;
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
{
|
{
|
||||||
@@ -260,8 +303,11 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
Logger.Log(@$"Failed to populate maximum statistics for {id}: {e}");
|
Logger.Log(@$"Failed to populate maximum statistics for {id}: {e}");
|
||||||
realmAccess.Write(r => r.Find<ScoreInfo>(id)!.BackgroundReprocessingFailed = true);
|
realmAccess.Write(r => r.Find<ScoreInfo>(id)!.BackgroundReprocessingFailed = true);
|
||||||
|
++failedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
completeNotification(notification, processedCount, scoreIds.Count, failedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void convertLegacyTotalScoreToStandardised()
|
private void convertLegacyTotalScoreToStandardised()
|
||||||
@@ -279,20 +325,17 @@ namespace osu.Game
|
|||||||
if (scoreIds.Count == 0)
|
if (scoreIds.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ProgressNotification notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
var notification = showProgressNotification(scoreIds.Count, "Upgrading scores to new scoring algorithm", "scores have been upgraded to the new scoring algorithm");
|
||||||
|
|
||||||
notificationOverlay?.Post(notification);
|
|
||||||
|
|
||||||
int processedCount = 0;
|
int processedCount = 0;
|
||||||
int failedCount = 0;
|
int failedCount = 0;
|
||||||
|
|
||||||
foreach (var id in scoreIds)
|
foreach (var id in scoreIds)
|
||||||
{
|
{
|
||||||
if (notification.State == ProgressNotificationState.Cancelled)
|
if (notification?.State == ProgressNotificationState.Cancelled)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
notification.Text = $"Upgrading scores to new scoring algorithm ({processedCount} of {scoreIds.Count})";
|
updateNotificationProgress(notification, processedCount, scoreIds.Count);
|
||||||
notification.Progress = (float)processedCount / scoreIds.Count;
|
|
||||||
|
|
||||||
sleepIfRequired();
|
sleepIfRequired();
|
||||||
|
|
||||||
@@ -310,7 +353,6 @@ namespace osu.Game
|
|||||||
s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION;
|
s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION;
|
||||||
});
|
});
|
||||||
|
|
||||||
Logger.Log($"Converted total score for score {id}");
|
|
||||||
++processedCount;
|
++processedCount;
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
@@ -325,24 +367,64 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processedCount == scoreIds.Count)
|
completeNotification(notification, processedCount, scoreIds.Count, failedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNotificationProgress(ProgressNotification? notification, int processedCount, int totalCount)
|
||||||
|
{
|
||||||
|
if (notification == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
notification.Text = notification.Text.ToString().Split('(').First().TrimEnd() + $" ({processedCount} of {totalCount})";
|
||||||
|
notification.Progress = (float)processedCount / totalCount;
|
||||||
|
|
||||||
|
if (processedCount % 100 == 0)
|
||||||
|
Logger.Log(notification.Text.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void completeNotification(ProgressNotification? notification, int processedCount, int totalCount, int? failedCount = null)
|
||||||
|
{
|
||||||
|
if (notification == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (processedCount == totalCount)
|
||||||
{
|
{
|
||||||
notification.CompletionText = $"{processedCount} score(s) have been upgraded to the new scoring algorithm";
|
notification.CompletionText = $"{processedCount} {notification.CompletionText}";
|
||||||
notification.Progress = 1;
|
notification.Progress = 1;
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
notification.Text = $"{processedCount} of {scoreIds.Count} score(s) have been upgraded to the new scoring algorithm.";
|
notification.Text = $"{processedCount} of {totalCount} {notification.CompletionText}";
|
||||||
|
|
||||||
// We may have arrived here due to user cancellation or completion with failures.
|
// We may have arrived here due to user cancellation or completion with failures.
|
||||||
if (failedCount > 0)
|
if (failedCount > 0)
|
||||||
notification.Text += $" Check logs for issues with {failedCount} failed upgrades.";
|
notification.Text += $" Check logs for issues with {failedCount} failed items.";
|
||||||
|
|
||||||
notification.State = ProgressNotificationState.Cancelled;
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProgressNotification? showProgressNotification(int totalCount, string running, string completed)
|
||||||
|
{
|
||||||
|
if (notificationOverlay == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (totalCount < 10)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ProgressNotification notification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Text = running,
|
||||||
|
CompletionText = completed,
|
||||||
|
State = ProgressNotificationState.Active
|
||||||
|
};
|
||||||
|
|
||||||
|
notificationOverlay?.Post(notification);
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
private void sleepIfRequired()
|
private void sleepIfRequired()
|
||||||
{
|
{
|
||||||
while (localUserPlayInfo?.IsPlaying.Value == true)
|
while (localUserPlayInfo?.IsPlaying.Value == true)
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap.StarRating = calculator.Calculate().StarRating;
|
beatmap.StarRating = calculator.Calculate().StarRating;
|
||||||
beatmap.Length = working.Beatmap.CalculatePlayableLength();
|
beatmap.Length = working.Beatmap.CalculatePlayableLength();
|
||||||
beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength();
|
beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength();
|
||||||
|
beatmap.EndTimeObjectCount = working.Beatmap.HitObjects.Count(h => h is IHasDuration);
|
||||||
|
beatmap.TotalObjectCount = working.Beatmap.HitObjects.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
||||||
|
|||||||
@@ -508,6 +508,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||||
TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic);
|
TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||||
|
|
||||||
|
if (customisationVisible.Value)
|
||||||
|
GetContainingInputManager().ChangeFocus(modSettingsArea);
|
||||||
|
else
|
||||||
|
Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -622,7 +627,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (textSearchStartsActive.Value)
|
if (textSearchStartsActive.Value)
|
||||||
SearchTextBox.TakeFocus();
|
SearchTextBox.HoldFocus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
@@ -761,11 +766,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`)
|
// TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`)
|
||||||
if (SearchTextBox.HasFocus)
|
SearchTextBox.HoldFocus = !SearchTextBox.HoldFocus;
|
||||||
SearchTextBox.KillFocus();
|
if (SearchTextBox.HoldFocus)
|
||||||
else
|
|
||||||
SearchTextBox.TakeFocus();
|
SearchTextBox.TakeFocus();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
public override bool AcceptsFocus => true;
|
||||||
|
|
||||||
public ModSettingsArea()
|
public ModSettingsArea()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
text = value;
|
text = value;
|
||||||
Schedule(() => textDrawable.Text = text);
|
Scheduler.AddOnce(t => textDrawable.Text = t, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
@@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override ModType Type => ModType.DifficultyReduction;
|
public override ModType Type => ModType.DifficultyReduction;
|
||||||
public override LocalisableString Description => "Whoaaaaa...";
|
public override LocalisableString Description => "Whoaaaaa...";
|
||||||
|
|
||||||
[SettingSource("Speed decrease", "The actual decrease to apply")]
|
[SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
|
||||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)
|
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)
|
||||||
{
|
{
|
||||||
MinValue = 0.5,
|
MinValue = 0.5,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Timing;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override ModType Type => ModType.DifficultyIncrease;
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
public override LocalisableString Description => "Uguuuuuuuu...";
|
public override LocalisableString Description => "Uguuuuuuuu...";
|
||||||
|
|
||||||
[SettingSource("Speed increase", "The actual increase to apply")]
|
[SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))]
|
||||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)
|
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)
|
||||||
{
|
{
|
||||||
MinValue = 1.01,
|
MinValue = 1.01,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The total count of non-filtered beatmaps displayed.
|
/// The total count of non-filtered beatmaps displayed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value));
|
public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.TotalItemsNotFiltered);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently selected beatmap set.
|
/// The currently selected beatmap set.
|
||||||
@@ -168,7 +168,10 @@ namespace osu.Game.Screens.Select
|
|||||||
applyActiveCriteria(false);
|
applyActiveCriteria(false);
|
||||||
|
|
||||||
if (loadedTestBeatmaps)
|
if (loadedTestBeatmaps)
|
||||||
signalBeatmapsLoaded();
|
{
|
||||||
|
invalidateAfterChange();
|
||||||
|
BeatmapSetsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Restore selection
|
// Restore selection
|
||||||
if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates))
|
if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates))
|
||||||
@@ -289,7 +292,7 @@ namespace osu.Game.Screens.Select
|
|||||||
foreach (var id in realmSets)
|
foreach (var id in realmSets)
|
||||||
{
|
{
|
||||||
if (!root.BeatmapSetsByID.ContainsKey(id))
|
if (!root.BeatmapSetsByID.ContainsKey(id))
|
||||||
UpdateBeatmapSet(realm.Realm.Find<BeatmapSetInfo>(id)!.Detach());
|
updateBeatmapSet(realm.Realm.Find<BeatmapSetInfo>(id)!.Detach());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in root.BeatmapSetsByID.Keys)
|
foreach (var id in root.BeatmapSetsByID.Keys)
|
||||||
@@ -298,15 +301,16 @@ namespace osu.Game.Screens.Select
|
|||||||
removeBeatmapSet(id);
|
removeBeatmapSet(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
signalBeatmapsLoaded();
|
invalidateAfterChange();
|
||||||
|
BeatmapSetsLoaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (int i in changes.NewModifiedIndices)
|
foreach (int i in changes.NewModifiedIndices)
|
||||||
UpdateBeatmapSet(sender[i].Detach());
|
updateBeatmapSet(sender[i].Detach());
|
||||||
|
|
||||||
foreach (int i in changes.InsertedIndices)
|
foreach (int i in changes.InsertedIndices)
|
||||||
UpdateBeatmapSet(sender[i].Detach());
|
updateBeatmapSet(sender[i].Detach());
|
||||||
|
|
||||||
if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null)
|
if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null)
|
||||||
{
|
{
|
||||||
@@ -347,6 +351,8 @@ namespace osu.Game.Screens.Select
|
|||||||
SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First());
|
SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidateAfterChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapsChanged(IRealmCollection<BeatmapInfo> sender, ChangeSet? changes)
|
private void beatmapsChanged(IRealmCollection<BeatmapInfo> sender, ChangeSet? changes)
|
||||||
@@ -355,6 +361,8 @@ namespace osu.Game.Screens.Select
|
|||||||
if (changes == null)
|
if (changes == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
foreach (int i in changes.InsertedIndices)
|
foreach (int i in changes.InsertedIndices)
|
||||||
{
|
{
|
||||||
var beatmapInfo = sender[i];
|
var beatmapInfo = sender[i];
|
||||||
@@ -367,17 +375,24 @@ namespace osu.Game.Screens.Select
|
|||||||
if (root.BeatmapSetsByID.TryGetValue(beatmapSet.ID, out var existingSets)
|
if (root.BeatmapSetsByID.TryGetValue(beatmapSet.ID, out var existingSets)
|
||||||
&& existingSets.SelectMany(s => s.Beatmaps).All(b => b.BeatmapInfo.ID != beatmapInfo.ID))
|
&& existingSets.SelectMany(s => s.Beatmaps).All(b => b.BeatmapInfo.ID != beatmapInfo.ID))
|
||||||
{
|
{
|
||||||
UpdateBeatmapSet(beatmapSet.Detach());
|
updateBeatmapSet(beatmapSet.Detach());
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
invalidateAfterChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IQueryable<BeatmapSetInfo> getBeatmapSets(Realm realm) => realm.All<BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected);
|
private IQueryable<BeatmapSetInfo> getBeatmapSets(Realm realm) => realm.All<BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected);
|
||||||
|
|
||||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) =>
|
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||||
|
{
|
||||||
removeBeatmapSet(beatmapSet.ID);
|
removeBeatmapSet(beatmapSet.ID);
|
||||||
|
invalidateAfterChange();
|
||||||
|
});
|
||||||
|
|
||||||
private void removeBeatmapSet(Guid beatmapSetID) => Schedule(() =>
|
private void removeBeatmapSet(Guid beatmapSetID)
|
||||||
{
|
{
|
||||||
if (!root.BeatmapSetsByID.TryGetValue(beatmapSetID, out var existingSets))
|
if (!root.BeatmapSetsByID.TryGetValue(beatmapSetID, out var existingSets))
|
||||||
return;
|
return;
|
||||||
@@ -392,16 +407,15 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
root.RemoveItem(set);
|
root.RemoveItem(set);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
itemsCache.Invalidate();
|
|
||||||
|
|
||||||
if (!Scroll.UserScrolling)
|
|
||||||
ScrollToSelected(true);
|
|
||||||
|
|
||||||
BeatmapSetsChanged?.Invoke();
|
|
||||||
});
|
|
||||||
|
|
||||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||||
|
{
|
||||||
|
updateBeatmapSet(beatmapSet);
|
||||||
|
invalidateAfterChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
private void updateBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
Guid? previouslySelectedID = null;
|
Guid? previouslySelectedID = null;
|
||||||
|
|
||||||
@@ -464,14 +478,7 @@ namespace osu.Game.Screens.Select
|
|||||||
select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
itemsCache.Invalidate();
|
|
||||||
|
|
||||||
if (!Scroll.UserScrolling)
|
|
||||||
ScrollToSelected(true);
|
|
||||||
|
|
||||||
BeatmapSetsChanged?.Invoke();
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects a given beatmap on the carousel.
|
/// Selects a given beatmap on the carousel.
|
||||||
@@ -748,15 +755,14 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void signalBeatmapsLoaded()
|
private void invalidateAfterChange()
|
||||||
{
|
{
|
||||||
if (!BeatmapSetsLoaded)
|
|
||||||
{
|
|
||||||
BeatmapSetsChanged?.Invoke();
|
|
||||||
BeatmapSetsLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemsCache.Invalidate();
|
itemsCache.Invalidate();
|
||||||
|
|
||||||
|
if (!Scroll.UserScrolling)
|
||||||
|
ScrollToSelected(true);
|
||||||
|
|
||||||
|
BeatmapSetsChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private float? scrollTarget;
|
private float? scrollTarget;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
|
|
||||||
public IReadOnlyList<CarouselItem> Items => items;
|
public IReadOnlyList<CarouselItem> Items => items;
|
||||||
|
|
||||||
|
public int TotalItemsNotFiltered { get; private set; }
|
||||||
|
|
||||||
private readonly List<CarouselItem> items = new List<CarouselItem>();
|
private readonly List<CarouselItem> items = new List<CarouselItem>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,6 +33,9 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
{
|
{
|
||||||
items.Remove(i);
|
items.Remove(i);
|
||||||
|
|
||||||
|
if (!i.Filtered.Value)
|
||||||
|
TotalItemsNotFiltered--;
|
||||||
|
|
||||||
// it's important we do the deselection after removing, so any further actions based on
|
// it's important we do the deselection after removing, so any further actions based on
|
||||||
// State.ValueChanged make decisions post-removal.
|
// State.ValueChanged make decisions post-removal.
|
||||||
i.State.Value = CarouselItemState.Collapsed;
|
i.State.Value = CarouselItemState.Collapsed;
|
||||||
@@ -55,6 +60,9 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
// criteria may be null for initial population. the filtering will be applied post-add.
|
// criteria may be null for initial population. the filtering will be applied post-add.
|
||||||
items.Add(i);
|
items.Add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!i.Filtered.Value)
|
||||||
|
TotalItemsNotFiltered++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CarouselGroup(List<CarouselItem>? items = null)
|
public CarouselGroup(List<CarouselItem>? items = null)
|
||||||
@@ -84,7 +92,14 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
{
|
{
|
||||||
base.Filter(criteria);
|
base.Filter(criteria);
|
||||||
|
|
||||||
items.ForEach(c => c.Filter(criteria));
|
TotalItemsNotFiltered = 0;
|
||||||
|
|
||||||
|
foreach (var c in items)
|
||||||
|
{
|
||||||
|
c.Filter(criteria);
|
||||||
|
if (!c.Filtered.Value)
|
||||||
|
TotalItemsNotFiltered++;
|
||||||
|
}
|
||||||
|
|
||||||
// Sorting is expensive, so only perform if it's actually changed.
|
// Sorting is expensive, so only perform if it's actually changed.
|
||||||
if (lastCriteria?.Sort != criteria.Sort)
|
if (lastCriteria?.Sort != criteria.Sort)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace osu.Game.Screens.Select
|
|||||||
BleedBottom = Footer.HEIGHT,
|
BleedBottom = Footer.HEIGHT,
|
||||||
SelectionChanged = updateSelectedBeatmap,
|
SelectionChanged = updateSelectedBeatmap,
|
||||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||||
FilterApplied = updateVisibleBeatmapCount,
|
FilterApplied = () => Scheduler.AddOnce(updateVisibleBeatmapCount),
|
||||||
GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s),
|
GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s),
|
||||||
}, c => carouselContainer.Child = c);
|
}, c => carouselContainer.Child = c);
|
||||||
|
|
||||||
@@ -843,7 +843,7 @@ namespace osu.Game.Screens.Select
|
|||||||
private void carouselBeatmapsLoaded()
|
private void carouselBeatmapsLoaded()
|
||||||
{
|
{
|
||||||
bindBindables();
|
bindBindables();
|
||||||
updateVisibleBeatmapCount();
|
Scheduler.AddOnce(updateVisibleBeatmapCount);
|
||||||
|
|
||||||
Carousel.AllowSelection = true;
|
Carousel.AllowSelection = true;
|
||||||
|
|
||||||
@@ -877,7 +877,8 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
// Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918
|
// Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918
|
||||||
// but also in this case we want support for formatting a number within a string).
|
// but also in this case we want support for formatting a number within a string).
|
||||||
FilterControl.InformationalText = Carousel.CountDisplayed != 1 ? $"{Carousel.CountDisplayed:#,0} matches" : $"{Carousel.CountDisplayed:#,0} match";
|
int carouselCountDisplayed = Carousel.CountDisplayed;
|
||||||
|
FilterControl.InformationalText = carouselCountDisplayed != 1 ? $"{carouselCountDisplayed:#,0} matches" : $"{carouselCountDisplayed:#,0} match";
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool boundLocalBindables;
|
private bool boundLocalBindables;
|
||||||
|
|||||||
Reference in New Issue
Block a user