diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 3248c5b8be..549423dfb8 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -91,6 +91,8 @@ namespace osu.Game.Screens.Edit private readonly HashSet pendingUpdates = new HashSet(); + private bool isBatchApplying; + /// /// Adds a collection of s to this . /// @@ -126,12 +128,17 @@ namespace osu.Game.Screens.Edit mutableHitObjects.Insert(index, hitObject); - // must be run after any change to hitobject ordering - beatmapProcessor?.PreProcess(); - processHitObject(hitObject); - beatmapProcessor?.PostProcess(); + if (isBatchApplying) + batchPendingInserts.Add(hitObject); + else + { + // must be run after any change to hitobject ordering + beatmapProcessor?.PreProcess(); + processHitObject(hitObject); + beatmapProcessor?.PostProcess(); - HitObjectAdded?.Invoke(hitObject); + HitObjectAdded?.Invoke(hitObject); + } } /// @@ -180,12 +187,58 @@ namespace osu.Game.Screens.Edit bindable.UnbindAll(); startTimeBindables.Remove(hitObject); - // must be run after any change to hitobject ordering + if (isBatchApplying) + batchPendingDeletes.Add(hitObject); + else + { + // must be run after any change to hitobject ordering + beatmapProcessor?.PreProcess(); + processHitObject(hitObject); + beatmapProcessor?.PostProcess(); + + HitObjectRemoved?.Invoke(hitObject); + } + } + + private readonly List batchPendingInserts = new List(); + + private readonly List batchPendingDeletes = new List(); + + /// + /// Apply a batch of operations in one go, without performing Pre/Postprocessing each time. + /// + /// The function which will apply the batch changes. + public void ApplyBatchChanges(Action applyFunction) + { + if (isBatchApplying) + throw new InvalidOperationException("Attempting to perform a batch application from within an existing batch"); + + isBatchApplying = true; + + applyFunction(this); + beatmapProcessor?.PreProcess(); - processHitObject(hitObject); beatmapProcessor?.PostProcess(); - HitObjectRemoved?.Invoke(hitObject); + isBatchApplying = false; + + foreach (var h in batchPendingDeletes) + { + processHitObject(h); + HitObjectRemoved?.Invoke(h); + } + + batchPendingDeletes.Clear(); + + foreach (var h in batchPendingInserts) + { + processHitObject(h); + HitObjectAdded?.Invoke(h); + } + + batchPendingInserts.Clear(); + + isBatchApplying = false; } /// diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 57b7ce6940..fb7d0dd826 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -68,16 +68,19 @@ namespace osu.Game.Screens.Edit toRemove.Sort(); toAdd.Sort(); - // Apply the changes. - for (int i = toRemove.Count - 1; i >= 0; i--) - editorBeatmap.RemoveAt(toRemove[i]); - - if (toAdd.Count > 0) + editorBeatmap.ApplyBatchChanges(eb => { - IBeatmap newBeatmap = readBeatmap(newState); - foreach (var i in toAdd) - editorBeatmap.Insert(i, newBeatmap.HitObjects[i]); - } + // Apply the changes. + for (int i = toRemove.Count - 1; i >= 0; i--) + eb.RemoveAt(toRemove[i]); + + if (toAdd.Count > 0) + { + IBeatmap newBeatmap = readBeatmap(newState); + foreach (var i in toAdd) + eb.Insert(i, newBeatmap.HitObjects[i]); + } + }); } private string readString(byte[] state) => Encoding.UTF8.GetString(state);