1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 17:27:24 +08:00

Merge pull request #28445 from bdach/mania-key-count-change-force-reload

Attempt full editor reload on key count change
This commit is contained in:
Dean Herbert 2024-06-12 14:42:59 +09:00 committed by GitHub
commit 90481223dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 255 additions and 11 deletions

View File

@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Tests.Visual;
using osuTK.Input;
namespace osu.Game.Rulesets.Mania.Tests.Editor
{
public partial class TestSceneManiaEditorSaving : EditorSavingTestScene
{
protected override Ruleset CreateRuleset() => new ManiaRuleset();
[Test]
public void TestKeyCountChange()
{
LabelledSliderBar<float> keyCount = null!;
AddStep("go to setup screen", () => InputManager.Key(Key.F4));
AddUntilStep("retrieve key count slider", () => keyCount = Editor.ChildrenOfType<SetupScreen>().Single().ChildrenOfType<LabelledSliderBar<float>>().First(), () => Is.Not.Null);
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("change key count to 8", () =>
{
keyCount.Current.Value = 8;
});
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
AddStep("refuse", () => InputManager.Key(Key.Number2));
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
AddStep("change key count to 8 again", () =>
{
keyCount.Current.Value = 8;
});
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
AddUntilStep("beatmap became 8K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(8));
}
}
}

View File

@ -3,20 +3,155 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
namespace osu.Game.Rulesets.Mania.Edit.Setup
{
public partial class ManiaDifficultySection : DifficultySection
public partial class ManiaDifficultySection : SetupSection
{
public override LocalisableString Title => EditorSetupStrings.DifficultyHeader;
private LabelledSliderBar<float> keyCountSlider { get; set; } = null!;
private LabelledSliderBar<float> healthDrainSlider { get; set; } = null!;
private LabelledSliderBar<float> overallDifficultySlider { get; set; } = null!;
private LabelledSliderBar<double> baseVelocitySlider { get; set; } = null!;
private LabelledSliderBar<double> tickRateSlider { get; set; } = null!;
[Resolved]
private Editor? editor { get; set; }
[Resolved]
private IEditorChangeHandler? changeHandler { get; set; }
[BackgroundDependencyLoader]
private void load()
{
CircleSizeSlider.Label = BeatmapsetsStrings.ShowStatsCsMania;
CircleSizeSlider.Description = "The number of columns in the beatmap";
if (CircleSizeSlider.Current is BindableNumber<float> circleSizeFloat)
circleSizeFloat.Precision = 1;
Children = new Drawable[]
{
keyCountSlider = new LabelledSliderBar<float>
{
Label = BeatmapsetsStrings.ShowStatsCsMania,
FixedLabelWidth = LABEL_WIDTH,
Description = "The number of columns in the beatmap",
Current = new BindableFloat(Beatmap.Difficulty.CircleSize)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 1,
}
},
healthDrainSlider = new LabelledSliderBar<float>
{
Label = BeatmapsetsStrings.ShowStatsDrain,
FixedLabelWidth = LABEL_WIDTH,
Description = EditorSetupStrings.DrainRateDescription,
Current = new BindableFloat(Beatmap.Difficulty.DrainRate)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 0.1f,
}
},
overallDifficultySlider = new LabelledSliderBar<float>
{
Label = BeatmapsetsStrings.ShowStatsAccuracy,
FixedLabelWidth = LABEL_WIDTH,
Description = EditorSetupStrings.OverallDifficultyDescription,
Current = new BindableFloat(Beatmap.Difficulty.OverallDifficulty)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 0.1f,
}
},
baseVelocitySlider = new LabelledSliderBar<double>
{
Label = EditorSetupStrings.BaseVelocity,
FixedLabelWidth = LABEL_WIDTH,
Description = EditorSetupStrings.BaseVelocityDescription,
Current = new BindableDouble(Beatmap.Difficulty.SliderMultiplier)
{
Default = 1.4,
MinValue = 0.4,
MaxValue = 3.6,
Precision = 0.01f,
}
},
tickRateSlider = new LabelledSliderBar<double>
{
Label = EditorSetupStrings.TickRate,
FixedLabelWidth = LABEL_WIDTH,
Description = EditorSetupStrings.TickRateDescription,
Current = new BindableDouble(Beatmap.Difficulty.SliderTickRate)
{
Default = 1,
MinValue = 1,
MaxValue = 4,
Precision = 1,
}
},
};
keyCountSlider.Current.BindValueChanged(updateKeyCount);
healthDrainSlider.Current.BindValueChanged(_ => updateValues());
overallDifficultySlider.Current.BindValueChanged(_ => updateValues());
baseVelocitySlider.Current.BindValueChanged(_ => updateValues());
tickRateSlider.Current.BindValueChanged(_ => updateValues());
}
private bool updatingKeyCount;
private void updateKeyCount(ValueChangedEvent<float> keyCount)
{
if (updatingKeyCount) return;
updateValues();
if (editor == null) return;
updatingKeyCount = true;
editor.Reload().ContinueWith(t =>
{
if (!t.GetResultSafely())
{
Schedule(() =>
{
changeHandler!.RestoreState(-1);
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value = keyCount.OldValue;
updatingKeyCount = false;
});
}
else
{
updatingKeyCount = false;
}
});
}
private void updateValues()
{
// for now, update these on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
Beatmap.Difficulty.CircleSize = keyCountSlider.Current.Value;
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
Beatmap.UpdateAllHitObjects();
Beatmap.SaveState();
}
}
}

View File

@ -421,7 +421,7 @@ namespace osu.Game.Rulesets.Mania
public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();
public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection();
public override SetupSection CreateEditorDifficultySection() => new ManiaDifficultySection();
public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList<Mod>? mods = null)
=> ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods);

View File

@ -49,6 +49,11 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString ContinueEditing => new TranslatableString(getKey(@"continue_editing"), @"Oops, continue editing");
/// <summary>
/// "The editor must be reloaded to apply this change. The beatmap will be saved."
/// </summary>
public static LocalisableString EditorReloadDialogHeader => new TranslatableString(getKey(@"editor_reload_dialog_header"), @"The editor must be reloaded to apply this change. The beatmap will be saved.");
private static string getKey(string key) => $@"{prefix}:{key}";
}
}

View File

@ -540,6 +540,8 @@ namespace osu.Game.Overlays.SkinEditor
protected void Redo() => changeHandler?.RestoreState(1);
void IEditorChangeHandler.RestoreState(int direction) => changeHandler?.RestoreState(direction);
public void Save(bool userTriggered = true) => save(currentSkin.Value, userTriggered);
private void save(Skin skin, bool userTriggered = true)

View File

@ -401,6 +401,6 @@ namespace osu.Game.Rulesets
/// <summary>
/// Can be overridden to alter the difficulty section to the editor beatmap setup screen.
/// </summary>
public virtual DifficultySection? CreateEditorDifficultySection() => null;
public virtual SetupSection? CreateEditorDifficultySection() => null;
}
}

View File

@ -1231,6 +1231,27 @@ namespace osu.Game.Screens.Edit
loader?.CancelPendingDifficultySwitch();
}
public Task<bool> Reload()
{
var tcs = new TaskCompletionSource<bool>();
dialogOverlay.Push(new ReloadEditorDialog(
reload: () =>
{
bool reloadedSuccessfully = attemptMutationOperation(() =>
{
if (!Save())
return false;
SwitchToDifficulty(editorBeatmap.BeatmapInfo);
return true;
});
tcs.SetResult(reloadedSuccessfully);
},
cancel: () => tcs.SetResult(false)));
return tcs.Task;
}
public void HandleTimestamp(string timestamp)
{
if (!EditorTimestampParser.TryParse(timestamp, out var timeSpan, out string selection))

View File

@ -83,10 +83,6 @@ namespace osu.Game.Screens.Edit
}
}
/// <summary>
/// Restores an older or newer state.
/// </summary>
/// <param name="direction">The direction to restore in. If less than 0, an older state will be used. If greater than 0, a newer state will be used.</param>
public void RestoreState(int direction)
{
if (TransactionActive)

View File

@ -43,5 +43,11 @@ namespace osu.Game.Screens.Edit
/// Note that this will be a no-op if there is a change in progress via <see cref="BeginChange"/>.
/// </summary>
void SaveState();
/// <summary>
/// Restores an older or newer state.
/// </summary>
/// <param name="direction">The direction to restore in. If less than 0, an older state will be used. If greater than 0, a newer state will be used.</param>
void RestoreState(int direction);
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Dialog;
using osu.Game.Localisation;
namespace osu.Game.Screens.Edit
{
public partial class ReloadEditorDialog : PopupDialog
{
public ReloadEditorDialog(Action reload, Action cancel)
{
HeaderText = EditorDialogsStrings.EditorReloadDialogHeader;
Icon = FontAwesome.Solid.Sync;
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton
{
Text = DialogStrings.Confirm,
Action = reload
},
new PopupDialogCancelButton
{
Text = DialogStrings.Cancel,
Action = cancel
}
};
}
}
}