1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 09:42:54 +08:00
This commit is contained in:
Dean Herbert 2024-12-02 20:36:21 -08:00 committed by GitHub
commit 9944897fea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 103 additions and 11 deletions

View File

@ -5,6 +5,7 @@
using System;
using System.Linq;
using Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
@ -23,6 +24,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.HitErrorMeters;
using osu.Game.Skinning;
using osu.Game.Tests.Beatmaps.IO;
@ -101,6 +103,77 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("current skin is mutable", () => !Game.Dependencies.Get<SkinManager>().CurrentSkin.Value.SkinInfo.Value.Protected);
}
[Test]
public void TestMutateProtectedSkinFromMainMenu_UndoToInitialStateIsCorrect()
{
AddStep("set default skin", () => Game.Dependencies.Get<SkinManager>().CurrentSkinInfo.SetDefault());
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
openSkinEditor();
AddUntilStep("current skin is mutable", () => !Game.Dependencies.Get<SkinManager>().CurrentSkin.Value.SkinInfo.Value.Protected);
AddUntilStep("wait for player", () =>
{
DismissAnyNotifications();
return Game.ScreenStack.CurrentScreen is Player;
});
string state = string.Empty;
AddUntilStep("wait for accuracy counter", () => Game.ChildrenOfType<ArgonAccuracyCounter>().Any(counter => counter.IsLoaded));
AddStep("dump state of accuracy meter", () => state = JsonConvert.SerializeObject(Game.ChildrenOfType<ArgonAccuracyCounter>().First().CreateSerialisedInfo()));
AddStep("add any component", () => Game.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First().TriggerClick());
AddStep("undo", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.Z);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("only one accuracy meter left",
() => Game.ChildrenOfType<Player>().Single().ChildrenOfType<ArgonAccuracyCounter>().Count(),
() => Is.EqualTo(1));
AddAssert("accuracy meter state unchanged",
() => JsonConvert.SerializeObject(Game.ChildrenOfType<ArgonAccuracyCounter>().First().CreateSerialisedInfo()),
() => Is.EqualTo(state));
}
[Test]
public void TestMutateProtectedSkinFromPlayer_UndoToInitialStateIsCorrect()
{
AddStep("set default skin", () => Game.Dependencies.Get<SkinManager>().CurrentSkinInfo.SetDefault());
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
advanceToSongSelect();
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
AddStep("enable NF", () => Game.SelectedMods.Value = new[] { new OsuModNoFail() });
AddStep("enter gameplay", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () =>
{
DismissAnyNotifications();
return Game.ScreenStack.CurrentScreen is Player;
});
openSkinEditor();
string state = string.Empty;
AddUntilStep("wait for accuracy counter", () => Game.ChildrenOfType<ArgonAccuracyCounter>().Any(counter => counter.IsLoaded));
AddStep("dump state of accuracy meter", () => state = JsonConvert.SerializeObject(Game.ChildrenOfType<ArgonAccuracyCounter>().First().CreateSerialisedInfo()));
AddStep("add any component", () => Game.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First().TriggerClick());
AddStep("undo", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.Z);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("only one accuracy meter left",
() => Game.ChildrenOfType<Player>().Single().ChildrenOfType<ArgonAccuracyCounter>().Count(),
() => Is.EqualTo(1));
AddAssert("accuracy meter state unchanged",
() => JsonConvert.SerializeObject(Game.ChildrenOfType<ArgonAccuracyCounter>().First().CreateSerialisedInfo()),
() => Is.EqualTo(state));
}
[Test]
public void TestComponentsDeselectedOnSkinEditorHide()
{

View File

@ -374,9 +374,10 @@ namespace osu.Game.Overlays.SkinEditor
return;
}
changeHandler = new SkinEditorChangeHandler(skinComponentsContainer);
changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
if (skinComponentsContainer.IsLoaded)
bindChangeHandler(skinComponentsContainer);
else
skinComponentsContainer.OnLoadComplete += d => Schedule(() => bindChangeHandler((SkinnableContainer)d));
content.Child = new SkinBlueprintContainer(skinComponentsContainer);
@ -418,10 +419,21 @@ namespace osu.Game.Overlays.SkinEditor
SelectedComponents.Clear();
placeComponent(component);
}
void bindChangeHandler(SkinnableContainer skinnableContainer)
{
changeHandler = new SkinEditorChangeHandler(skinnableContainer);
changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
}
}
private void skinChanged()
{
if (skins.EnsureMutableSkin())
// Another skin changed event will arrive which will complete the process.
return;
headerText.Clear();
headerText.AddParagraph(SkinEditorStrings.SkinEditor, cp => cp.Font = OsuFont.Default.With(size: 16));
@ -439,17 +451,24 @@ namespace osu.Game.Overlays.SkinEditor
});
changeHandler?.Dispose();
changeHandler = null;
skins.EnsureMutableSkin();
// Schedule is required to ensure that all layout in `LoadComplete` methods has been completed
// before storing an undo state.
//
// See https://github.com/ppy/osu/blob/8e6a4559e3ae8c9892866cf9cf8d4e8d1b72afd0/osu.Game/Skinning/SkinReloadableDrawable.cs#L76.
Schedule(() =>
{
var targetContainer = getTarget(selectedTarget.Value);
var targetContainer = getTarget(selectedTarget.Value);
if (targetContainer != null)
changeHandler = new SkinEditorChangeHandler(targetContainer);
if (targetContainer != null)
changeHandler = new SkinEditorChangeHandler(targetContainer);
hasBegunMutating = true;
hasBegunMutating = true;
// Reload sidebar components.
selectedTarget.TriggerChange();
// Reload sidebar components.
selectedTarget.TriggerChange();
});
}
/// <summary>

View File

@ -34,7 +34,7 @@ namespace osu.Game.Overlays.SkinEditor
return;
components = new BindableList<ISerialisableDrawable> { BindTarget = firstTarget.Components };
components.BindCollectionChanged((_, _) => SaveState());
components.BindCollectionChanged((_, _) => SaveState(), true);
}
protected override void WriteCurrentStateToStream(MemoryStream stream)