diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs
index 9d470f58f1..95b88b141f 100644
--- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs
+++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs
@@ -556,11 +556,46 @@ namespace osu.Game.Overlays.SkinEditor
changeHandler?.BeginChange();
foreach (var item in items)
- availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item);
+ availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item, true);
changeHandler?.EndChange();
}
+ public void BringSelectionToFront()
+ {
+ if (getTarget(selectedTarget.Value) is not SkinComponentsContainer target)
+ return;
+
+ // Iterating by target components order ensures we maintain the same order across selected components, regardless
+ // of the order they were selected in.
+ foreach (var d in target.Components.ToArray())
+ {
+ if (!SelectedComponents.Contains(d))
+ continue;
+
+ target.Remove(d, false);
+
+ // Selection would be reset by the remove.
+ SelectedComponents.Add(d);
+ target.Add(d);
+ }
+ }
+
+ public void SendSelectionToBack()
+ {
+ if (getTarget(selectedTarget.Value) is not SkinComponentsContainer target)
+ return;
+
+ foreach (var d in target.Components.ToArray())
+ {
+ if (SelectedComponents.Contains(d))
+ continue;
+
+ target.Remove(d, false);
+ target.Add(d);
+ }
+ }
+
#region Drag & drop import handling
public Task Import(params string[] paths)
diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs
index c628ad8480..b43f4eeb00 100644
--- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs
+++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs
@@ -13,6 +13,7 @@ using osu.Framework.Utils;
using osu.Game.Extensions;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
+using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Skinning;
using osuTK;
@@ -206,6 +207,14 @@ namespace osu.Game.Overlays.SkinEditor
((Drawable)blueprint.Item).Position = Vector2.Zero;
});
+ yield return new EditorMenuItemSpacer();
+
+ yield return new OsuMenuItem("Bring to front", MenuItemType.Standard, () => skinEditor.BringSelectionToFront());
+
+ yield return new OsuMenuItem("Send to back", MenuItemType.Standard, () => skinEditor.SendSelectionToBack());
+
+ yield return new EditorMenuItemSpacer();
+
foreach (var item in base.GetContextMenuItemsForSelection(selection))
yield return item;
diff --git a/osu.Game/Skinning/ISerialisableDrawableContainer.cs b/osu.Game/Skinning/ISerialisableDrawableContainer.cs
index 9f93d8a2e3..a19c8c5162 100644
--- a/osu.Game/Skinning/ISerialisableDrawableContainer.cs
+++ b/osu.Game/Skinning/ISerialisableDrawableContainer.cs
@@ -45,6 +45,7 @@ namespace osu.Game.Skinning
/// Remove an existing skinnable component from this target.
///
/// The component to remove.
- void Remove(ISerialisableDrawable component);
+ /// Whether removed items should be immediately disposed.
+ void Remove(ISerialisableDrawable component, bool disposeImmediately);
}
}
diff --git a/osu.Game/Skinning/SkinComponentsContainer.cs b/osu.Game/Skinning/SkinComponentsContainer.cs
index d18e9023cd..adf0a288b4 100644
--- a/osu.Game/Skinning/SkinComponentsContainer.cs
+++ b/osu.Game/Skinning/SkinComponentsContainer.cs
@@ -100,7 +100,7 @@ namespace osu.Game.Skinning
///
/// Thrown when attempting to add an element to a target which is not supported by the current skin.
/// Thrown if the provided instance is not a .
- public void Remove(ISerialisableDrawable component)
+ public void Remove(ISerialisableDrawable component, bool disposeImmediately)
{
if (content == null)
throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin.");
@@ -108,7 +108,7 @@ namespace osu.Game.Skinning
if (!(component is Drawable drawable))
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component));
- content.Remove(drawable, true);
+ content.Remove(drawable, disposeImmediately);
components.Remove(component);
}