From 16c8a392a1c7c73d0aeae9aa02edcd1a6e4d2f7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Feb 2023 17:45:38 +0900 Subject: [PATCH 1/8] Add ability to send selected skin components to front or back --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 37 ++++++++++++++++++- .../SkinEditor/SkinSelectionHandler.cs | 9 +++++ .../ISerialisableDrawableContainer.cs | 3 +- osu.Game/Skinning/SkinComponentsContainer.cs | 4 +- 4 files changed, 49 insertions(+), 4 deletions(-) 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); } From 90ca635a1771d9953f3891656296b7b7d98dda84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Feb 2023 17:56:59 +0900 Subject: [PATCH 2/8] Fix weird nullability in `TestSceneSkinEditor` --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 2f20d75813..83609b9ae7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -4,6 +4,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; @@ -21,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay { public partial class TestSceneSkinEditor : PlayerTestScene { - private SkinEditor? skinEditor; + private SkinEditor skinEditor = null!; protected override bool Autoplay => true; @@ -40,17 +41,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("reload skin editor", () => { - skinEditor?.Expire(); + if (skinEditor.IsNotNull()) + skinEditor.Expire(); Player.ScaleTo(0.4f); LoadComponentAsync(skinEditor = new SkinEditor(Player), Add); }); - AddUntilStep("wait for loaded", () => skinEditor!.IsLoaded); + AddUntilStep("wait for loaded", () => skinEditor.IsLoaded); } [Test] public void TestToggleEditor() { - AddToggleStep("toggle editor visibility", _ => skinEditor!.ToggleVisibility()); + AddToggleStep("toggle editor visibility", _ => skinEditor.ToggleVisibility()); } [Test] @@ -63,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay var blueprint = skinEditor.ChildrenOfType().First(b => b.Item is BarHitErrorMeter); hitErrorMeter = (BarHitErrorMeter)blueprint.Item; - skinEditor!.SelectedComponents.Clear(); + skinEditor.SelectedComponents.Clear(); skinEditor.SelectedComponents.Add(blueprint.Item); }); From 32a9c066dfd2a02c0cc40f5554417ab5bd8e8c97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Feb 2023 18:17:03 +0900 Subject: [PATCH 3/8] Add test coverage of bring-to-front / send-to-back operations --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 83609b9ae7..9690d00d4c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -32,12 +33,14 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] public readonly EditorClipboard Clipboard = new EditorClipboard(); + private SkinComponentsContainer targetContainer => Player.ChildrenOfType().First(); + [SetUpSteps] public override void SetUpSteps() { base.SetUpSteps(); - AddUntilStep("wait for hud load", () => Player.ChildrenOfType().All(c => c.ComponentsLoaded)); + AddUntilStep("wait for hud load", () => targetContainer.ComponentsLoaded); AddStep("reload skin editor", () => { @@ -49,6 +52,52 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for loaded", () => skinEditor.IsLoaded); } + [TestCase(false)] + [TestCase(true)] + public void TestBringToFront(bool alterSelectionOrder) + { + AddAssert("Ensure over three components available", () => targetContainer.Components.Count, () => Is.GreaterThan(3)); + + IEnumerable originalOrder = null!; + + AddStep("Save order of components before operation", () => originalOrder = targetContainer.Components.Take(3).ToArray()); + + if (alterSelectionOrder) + AddStep("Select first three components in reverse order", () => skinEditor.SelectedComponents.AddRange(originalOrder.Reverse())); + else + AddStep("Select first three components", () => skinEditor.SelectedComponents.AddRange(originalOrder)); + + AddAssert("Components are not front-most", () => targetContainer.Components.TakeLast(3).ToArray(), () => Is.Not.EqualTo(skinEditor.SelectedComponents)); + + AddStep("Bring to front", () => skinEditor.BringSelectionToFront()); + AddAssert("Ensure components are now front-most in original order", () => targetContainer.Components.TakeLast(3).ToArray(), () => Is.EqualTo(originalOrder)); + AddStep("Bring to front again", () => skinEditor.BringSelectionToFront()); + AddAssert("Ensure components are still front-most in original order", () => targetContainer.Components.TakeLast(3).ToArray(), () => Is.EqualTo(originalOrder)); + } + + [TestCase(false)] + [TestCase(true)] + public void TestSendToBack(bool alterSelectionOrder) + { + AddAssert("Ensure over three components available", () => targetContainer.Components.Count, () => Is.GreaterThan(3)); + + IEnumerable originalOrder = null!; + + AddStep("Save order of components before operation", () => originalOrder = targetContainer.Components.TakeLast(3).ToArray()); + + if (alterSelectionOrder) + AddStep("Select last three components in reverse order", () => skinEditor.SelectedComponents.AddRange(originalOrder.Reverse())); + else + AddStep("Select last three components", () => skinEditor.SelectedComponents.AddRange(originalOrder)); + + AddAssert("Components are not back-most", () => targetContainer.Components.Take(3).ToArray(), () => Is.Not.EqualTo(skinEditor.SelectedComponents)); + + AddStep("Send to back", () => skinEditor.SendSelectionToBack()); + AddAssert("Ensure components are now back-most in original order", () => targetContainer.Components.Take(3).ToArray(), () => Is.EqualTo(originalOrder)); + AddStep("Send to back again", () => skinEditor.SendSelectionToBack()); + AddAssert("Ensure components are still back-most in original order", () => targetContainer.Components.Take(3).ToArray(), () => Is.EqualTo(originalOrder)); + } + [Test] public void TestToggleEditor() { From c48aceb055433f57bd00d885691d8a61e8317460 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Feb 2023 20:03:36 +0900 Subject: [PATCH 4/8] Fix undo history not being batched correctly for depth change operations --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 95b88b141f..02ede8c08b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -566,6 +566,8 @@ namespace osu.Game.Overlays.SkinEditor if (getTarget(selectedTarget.Value) is not SkinComponentsContainer target) return; + changeHandler?.BeginChange(); + // 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()) @@ -579,6 +581,8 @@ namespace osu.Game.Overlays.SkinEditor SelectedComponents.Add(d); target.Add(d); } + + changeHandler?.EndChange(); } public void SendSelectionToBack() @@ -586,6 +590,8 @@ namespace osu.Game.Overlays.SkinEditor if (getTarget(selectedTarget.Value) is not SkinComponentsContainer target) return; + changeHandler?.BeginChange(); + foreach (var d in target.Components.ToArray()) { if (SelectedComponents.Contains(d)) @@ -594,6 +600,8 @@ namespace osu.Game.Overlays.SkinEditor target.Remove(d, false); target.Add(d); } + + changeHandler?.EndChange(); } #region Drag & drop import handling From dc3c1150b8401b4f4d49b1ecb6c8ba30663fe217 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Feb 2023 21:10:15 +0900 Subject: [PATCH 5/8] Set better defaults for `SkinBlueprint` transforms --- osu.Game/Overlays/SkinEditor/SkinBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs index 034ca11c5c..c090878899 100644 --- a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs +++ b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs @@ -82,6 +82,7 @@ namespace osu.Game.Overlays.SkinEditor { Text = Item.GetType().Name, Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold), + Alpha = 0, Anchor = Anchor.BottomRight, Origin = Anchor.TopRight, }, @@ -99,7 +100,6 @@ namespace osu.Game.Overlays.SkinEditor base.LoadComplete(); updateSelectedState(); - this.FadeInFromZero(200, Easing.OutQuint); } protected override bool OnHover(HoverEvent e) From 8d747f240220e98cb40a9edd567f6778c31115e0 Mon Sep 17 00:00:00 2001 From: OpenSauce <48618519+OpenSauce04@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:06:33 +0000 Subject: [PATCH 6/8] Fixed grammar mistake in Taiko Relax mod description --- osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs index d1e9ab1428..69a98f8372 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRelax.cs @@ -8,6 +8,6 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModRelax : ModRelax { - public override LocalisableString Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's."; + public override LocalisableString Description => @"No ninja-like spinners, demanding drumrolls or unexpected katus."; } } From bd1460a8d5726a67acfdfbfb08568b872fcc8d83 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Feb 2023 14:32:58 +0900 Subject: [PATCH 7/8] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 4bdf5d68c5..abd8406cf7 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - +