diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs index 05ed03f01d..13f83eac96 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModPresetColumn.cs @@ -11,9 +11,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Mania.Mods; @@ -35,16 +37,27 @@ namespace osu.Game.Tests.Visual.UserInterface [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + [Cached(typeof(IDialogOverlay))] + private readonly DialogOverlay dialogOverlay = new DialogOverlay(); + [BackgroundDependencyLoader] private void load() { Dependencies.Cache(rulesets = new RealmRulesetStore(Realm)); Dependencies.Cache(Realm); - base.Content.Add(content = new PopoverContainer + base.Content.AddRange(new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(30), + new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = content = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(30), + } + }, + dialogOverlay }); } @@ -205,6 +218,46 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("popover closed", () => !this.ChildrenOfType().Any()); } + [Test] + public void TestDeleteFlow() + { + ModPresetColumn modPresetColumn = null!; + + AddStep("create content", () => Child = modPresetColumn = new ModPresetColumn + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded); + AddStep("right click first panel", () => + { + var panel = this.ChildrenOfType().First(); + InputManager.MoveMouseTo(panel); + InputManager.Click(MouseButton.Right); + }); + + AddUntilStep("wait for context menu", () => this.ChildrenOfType().Any()); + AddStep("click delete", () => + { + var deleteItem = this.ChildrenOfType().Single(); + InputManager.MoveMouseTo(deleteItem); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for dialog", () => dialogOverlay.CurrentDialog is DeleteModPresetDialog); + AddStep("hold confirm", () => + { + var confirmButton = this.ChildrenOfType().Single(); + InputManager.MoveMouseTo(confirmButton); + InputManager.PressButton(MouseButton.Left); + }); + AddUntilStep("wait for dialog to close", () => dialogOverlay.CurrentDialog == null); + AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left)); + AddUntilStep("preset deletion occurred", () => this.ChildrenOfType().Count() == 2); + AddAssert("preset soft-deleted", () => Realm.Run(r => r.All().Count(preset => preset.DeletePending) == 1)); + } + private ICollection createTestPresets() => new[] { new ModPreset diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 5f0ec67c71..1fb1c1da5c 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -25,6 +25,7 @@ using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Models; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; using Realms; @@ -292,6 +293,11 @@ namespace osu.Game.Database foreach (var s in pendingDeleteSkins) realm.Remove(s); + var pendingDeletePresets = realm.All().Where(s => s.DeletePending); + + foreach (var s in pendingDeletePresets) + realm.Remove(s); + transaction.Commit(); } diff --git a/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs new file mode 100644 index 0000000000..b3a19e35ce --- /dev/null +++ b/osu.Game/Overlays/Mods/DeleteModPresetDialog.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Database; +using osu.Game.Overlays.Dialog; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Overlays.Mods +{ + public class DeleteModPresetDialog : DeleteConfirmationDialog + { + public DeleteModPresetDialog(Live modPreset) + { + BodyText = modPreset.PerformRead(preset => preset.Name); + DeleteAction = () => modPreset.PerformWrite(preset => preset.DeletePending = true); + } + } +} diff --git a/osu.Game/Overlays/Mods/ModPresetPanel.cs b/osu.Game/Overlays/Mods/ModPresetPanel.cs index a00729d9fd..49e7d55bca 100644 --- a/osu.Game/Overlays/Mods/ModPresetPanel.cs +++ b/osu.Game/Overlays/Mods/ModPresetPanel.cs @@ -4,18 +4,24 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; using osu.Game.Database; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { - public class ModPresetPanel : ModSelectPanel, IHasCustomTooltip + public class ModPresetPanel : ModSelectPanel, IHasCustomTooltip, IHasContextMenu { public readonly Live Preset; public override BindableBool Active { get; } = new BindableBool(); + [Resolved] + private IDialogOverlay? dialogOverlay { get; set; } + public ModPresetPanel(Live preset) { Preset = preset; @@ -30,7 +36,20 @@ namespace osu.Game.Overlays.Mods AccentColour = colours.Orange1; } + #region IHasCustomTooltip + public ModPreset TooltipContent => Preset.Value; public ITooltip GetCustomTooltip() => new ModPresetTooltip(ColourProvider); + + #endregion + + #region IHasContextMenu + + public MenuItem[] ContextMenuItems => new MenuItem[] + { + new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new DeleteModPresetDialog(Preset))) + }; + + #endregion } }