mirror of
https://github.com/ppy/osu.git
synced 2025-03-10 15:37:20 +08:00
Add "discard unsaved changes" operation to beatmap editor
Apparently useful in modding workflows when you want to test out a few different variants of a thing. Re-uses `Ctrl-L` binding from stable. Some folks may argue that the dialog makes the hotkey pointless, but I really do want to protect users from accidental data loss, and also if you want to power through it quickly, you can hit the 1 key when the dialog shows, which will bypass the hold-to-activate period (which wasn't intentional, but so many people want a bypass at this point that we're probably keeping that behaviour for power users).
This commit is contained in:
parent
f6cf63edae
commit
3f461c0734
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
keyCount.Current.Value = 8;
|
keyCount.Current.Value = 8;
|
||||||
});
|
});
|
||||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
|
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().SingleOrDefault()?.CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
|
||||||
AddStep("refuse", () => InputManager.Key(Key.Number2));
|
AddStep("refuse", () => InputManager.Key(Key.Number2));
|
||||||
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
|
AddAssert("key count is 5", () => keyCount.Current.Value, () => Is.EqualTo(5));
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
|||||||
{
|
{
|
||||||
keyCount.Current.Value = 8;
|
keyCount.Current.Value = 8;
|
||||||
});
|
});
|
||||||
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<ReloadEditorDialog>);
|
AddUntilStep("dialog visible", () => Game.ChildrenOfType<IDialogOverlay>().Single().CurrentDialog, Is.InstanceOf<SaveAndReloadEditorDialog>);
|
||||||
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
|
AddStep("acquiesce", () => InputManager.Key(Key.Number1));
|
||||||
AddUntilStep("beatmap became 8K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(8));
|
AddUntilStep("beatmap became 8K", () => Game.Beatmap.Value.BeatmapInfo.Difficulty.CircleSize, () => Is.EqualTo(8));
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
|
|
||||||
updatingKeyCount = true;
|
updatingKeyCount = true;
|
||||||
|
|
||||||
editor.Reload().ContinueWith(t =>
|
editor.SaveAndReload().ContinueWith(t =>
|
||||||
{
|
{
|
||||||
if (!t.GetResultSafely())
|
if (!t.GetResultSafely())
|
||||||
{
|
{
|
||||||
|
@ -155,6 +155,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.B }, GlobalAction.EditorRemoveClosestBookmark),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.B }, GlobalAction.EditorRemoveClosestBookmark),
|
||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Left }, GlobalAction.EditorSeekToPreviousBookmark),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Left }, GlobalAction.EditorSeekToPreviousBookmark),
|
||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Right }, GlobalAction.EditorSeekToNextBookmark),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Right }, GlobalAction.EditorSeekToNextBookmark),
|
||||||
|
new KeyBinding(new[] { InputKey.Control, InputKey.L }, GlobalAction.EditorDiscardUnsavedChanges),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static IEnumerable<KeyBinding> editorTestPlayKeyBindings => new[]
|
private static IEnumerable<KeyBinding> editorTestPlayKeyBindings => new[]
|
||||||
@ -502,6 +503,9 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleMoveControl))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleMoveControl))]
|
||||||
EditorToggleMoveControl,
|
EditorToggleMoveControl,
|
||||||
|
|
||||||
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDiscardUnsavedChanges))]
|
||||||
|
EditorDiscardUnsavedChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GlobalActionCategory
|
public enum GlobalActionCategory
|
||||||
|
@ -54,6 +54,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </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.");
|
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.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Discard all unsaved changes? This cannot be undone."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DiscardUnsavedChangesDialogHeader => new TranslatableString(getKey(@"discard_unsaved_changes_dialog_header"), @"Discard all unsaved changes? This cannot be undone.");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,6 +459,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString EditorToggleMoveControl => new TranslatableString(getKey(@"editor_toggle_move_control"), @"Toggle movement control");
|
public static LocalisableString EditorToggleMoveControl => new TranslatableString(getKey(@"editor_toggle_move_control"), @"Toggle movement control");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Discard unsaved changes"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString EditorDiscardUnsavedChanges => new TranslatableString(getKey(@"editor_discard_unsaved_changes"), @"Discard unsaved changes");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
osu.Game/Screens/Edit/DiscardUnsavedChangesDialog.cs
Normal file
33
osu.Game/Screens/Edit/DiscardUnsavedChangesDialog.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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.Localisation;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public partial class DiscardUnsavedChangesDialog : PopupDialog
|
||||||
|
{
|
||||||
|
public DiscardUnsavedChangesDialog(Action exit)
|
||||||
|
{
|
||||||
|
HeaderText = EditorDialogsStrings.DiscardUnsavedChangesDialogHeader;
|
||||||
|
|
||||||
|
Icon = FontAwesome.Solid.Trash;
|
||||||
|
|
||||||
|
Buttons = new PopupDialogButton[]
|
||||||
|
{
|
||||||
|
new PopupDialogDangerousButton
|
||||||
|
{
|
||||||
|
Text = EditorDialogsStrings.ForgetAllChanges,
|
||||||
|
Action = exit
|
||||||
|
},
|
||||||
|
new PopupDialogCancelButton
|
||||||
|
{
|
||||||
|
Text = EditorDialogsStrings.ContinueEditing,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -164,6 +164,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
private bool switchingDifficulty;
|
private bool switchingDifficulty;
|
||||||
|
|
||||||
private string lastSavedHash;
|
private string lastSavedHash;
|
||||||
|
private EditorMenuItem discardChangesMenuItem;
|
||||||
|
|
||||||
private ScreenContainer screenContainer;
|
private ScreenContainer screenContainer;
|
||||||
|
|
||||||
@ -391,6 +392,10 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo) { Hotkey = new Hotkey(PlatformAction.Undo) },
|
undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo) { Hotkey = new Hotkey(PlatformAction.Undo) },
|
||||||
redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo) { Hotkey = new Hotkey(PlatformAction.Redo) },
|
redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo) { Hotkey = new Hotkey(PlatformAction.Redo) },
|
||||||
|
discardChangesMenuItem = new EditorMenuItem("Discard unsaved changes", MenuItemType.Destructive, DiscardUnsavedChanges)
|
||||||
|
{
|
||||||
|
Hotkey = new Hotkey(GlobalAction.EditorDiscardUnsavedChanges)
|
||||||
|
},
|
||||||
new OsuMenuItemSpacer(),
|
new OsuMenuItemSpacer(),
|
||||||
cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut) { Hotkey = new Hotkey(PlatformAction.Cut) },
|
cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut) { Hotkey = new Hotkey(PlatformAction.Cut) },
|
||||||
copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy) { Hotkey = new Hotkey(PlatformAction.Copy) },
|
copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy) { Hotkey = new Hotkey(PlatformAction.Copy) },
|
||||||
@ -607,6 +612,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
clock.ProcessFrame();
|
clock.ProcessFrame();
|
||||||
|
|
||||||
|
discardChangesMenuItem.Action.Disabled = !HasUnsavedChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||||
@ -821,6 +828,10 @@ namespace osu.Game.Screens.Edit
|
|||||||
case GlobalAction.EditorTestGameplay:
|
case GlobalAction.EditorTestGameplay:
|
||||||
bottomBar.TestGameplayButton.TriggerClick();
|
bottomBar.TestGameplayButton.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.EditorDiscardUnsavedChanges:
|
||||||
|
DiscardUnsavedChanges();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1008,6 +1019,20 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
protected void Redo() => changeHandler?.RestoreState(1);
|
protected void Redo() => changeHandler?.RestoreState(1);
|
||||||
|
|
||||||
|
protected void DiscardUnsavedChanges()
|
||||||
|
{
|
||||||
|
if (!HasUnsavedChanges)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// we're not doing this via `changeHandler` because `changeHandler` has limited number of undo actions
|
||||||
|
// and therefore there's no guarantee that it even *has* the beatmap's last saved state in its history still.
|
||||||
|
dialogOverlay.Push(new DiscardUnsavedChangesDialog(() =>
|
||||||
|
{
|
||||||
|
updateLastSavedHash(); // without this a second dialog will show (the standard "save unsaved changes" one that shows on exit).
|
||||||
|
SwitchToDifficulty(editorBeatmap.BeatmapInfo);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
protected void SetPreviewPointToCurrentTime()
|
protected void SetPreviewPointToCurrentTime()
|
||||||
{
|
{
|
||||||
editorBeatmap.PreviewTime.Value = (int)clock.CurrentTime;
|
editorBeatmap.PreviewTime.Value = (int)clock.CurrentTime;
|
||||||
@ -1510,11 +1535,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
loader?.CancelPendingDifficultySwitch();
|
loader?.CancelPendingDifficultySwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> Reload()
|
public Task<bool> SaveAndReload()
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
dialogOverlay.Push(new ReloadEditorDialog(
|
dialogOverlay.Push(new SaveAndReloadEditorDialog(
|
||||||
reload: () =>
|
reload: () =>
|
||||||
{
|
{
|
||||||
bool reloadedSuccessfully = attemptMutationOperation(() =>
|
bool reloadedSuccessfully = attemptMutationOperation(() =>
|
||||||
|
@ -8,9 +8,9 @@ using osu.Game.Localisation;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
public partial class ReloadEditorDialog : PopupDialog
|
public partial class SaveAndReloadEditorDialog : PopupDialog
|
||||||
{
|
{
|
||||||
public ReloadEditorDialog(Action reload, Action cancel)
|
public SaveAndReloadEditorDialog(Action reload, Action cancel)
|
||||||
{
|
{
|
||||||
HeaderText = EditorDialogsStrings.EditorReloadDialogHeader;
|
HeaderText = EditorDialogsStrings.EditorReloadDialogHeader;
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user