mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 12:33:01 +08:00
Merge pull request #20901 from peppy/add-editor-object-clone
Add ability to clone objects in the editor
This commit is contained in:
commit
181c7d1d6e
@ -155,6 +155,20 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddUntilStep("composer selection box is visible", () => Editor.ChildrenOfType<HitObjectComposer>().First().ChildrenOfType<EditorSelectionHandler>().First().Alpha > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClone()
|
||||
{
|
||||
var addedObject = new HitCircle { StartTime = 1000 };
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||
|
||||
AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||
AddStep("clone", () => Editor.Clone());
|
||||
AddAssert("is two objects", () => EditorBeatmap.HitObjects.Count == 2);
|
||||
AddStep("clone", () => Editor.Clone());
|
||||
AddAssert("is three objects", () => EditorBeatmap.HitObjects.Count == 3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCutNothing()
|
||||
{
|
||||
@ -175,5 +189,22 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddStep("paste hitobject", () => Editor.Paste());
|
||||
AddAssert("are no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCloneNothing()
|
||||
{
|
||||
// Add arbitrary object and copy to clipboard.
|
||||
// This is tested to ensure that clone doesn't incorrectly read from the clipboard when no selection is made.
|
||||
var addedObject = new HitCircle { StartTime = 1000 };
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||
AddStep("copy hitobject", () => Editor.Copy());
|
||||
|
||||
AddStep("deselect all objects", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||
AddStep("clone", () => Editor.Clone());
|
||||
AddAssert("still one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,17 @@ namespace osu.Game.Input.Bindings
|
||||
parentInputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
// IMPORTANT: Do not change the order of key bindings in this list.
|
||||
// It is used to decide the order of precedence (see note in DatabasedKeyBindingContainer).
|
||||
// IMPORTANT: Take care when changing order of the items in the enumerable.
|
||||
// It is used to decide the order of precedence, with the earlier items having higher precedence.
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings
|
||||
.Concat(OverlayKeyBindings)
|
||||
.Concat(EditorKeyBindings)
|
||||
.Concat(InGameKeyBindings)
|
||||
.Concat(SongSelectKeyBindings)
|
||||
.Concat(AudioControlKeyBindings);
|
||||
.Concat(AudioControlKeyBindings)
|
||||
// Overlay bindings may conflict with more local cases like the editor so they are checked last.
|
||||
// It has generally been agreed on that local screens like the editor should have priority,
|
||||
// based on such usages potentially requiring a lot more key bindings that may be "shared" with global ones.
|
||||
.Concat(OverlayKeyBindings);
|
||||
|
||||
public IEnumerable<KeyBinding> GlobalKeyBindings => new[]
|
||||
{
|
||||
@ -87,6 +90,7 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.F3 }, GlobalAction.EditorTimingMode),
|
||||
new KeyBinding(new[] { InputKey.F4 }, GlobalAction.EditorSetupMode),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.EditorCloneSelection),
|
||||
new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft),
|
||||
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
||||
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
||||
@ -343,5 +347,8 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleProfile))]
|
||||
ToggleProfile,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCloneSelection))]
|
||||
EditorCloneSelection
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +184,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString EditorTapForBPM => new TranslatableString(getKey(@"editor_tap_for_bpm"), @"Tap for BPM");
|
||||
|
||||
/// <summary>
|
||||
/// "Clone selection"
|
||||
/// </summary>
|
||||
public static LocalisableString EditorCloneSelection => new TranslatableString(getKey(@"editor_clone_selection"), @"Clone selection");
|
||||
|
||||
/// <summary>
|
||||
/// "Cycle grid display mode"
|
||||
/// </summary>
|
||||
|
@ -304,6 +304,7 @@ namespace osu.Game.Screens.Edit
|
||||
cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut),
|
||||
copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy),
|
||||
pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste),
|
||||
cloneMenuItem = new EditorMenuItem("Clone", MenuItemType.Standard, Clone),
|
||||
}
|
||||
},
|
||||
new MenuItem("View")
|
||||
@ -575,6 +576,10 @@ namespace osu.Game.Screens.Edit
|
||||
this.Exit();
|
||||
return true;
|
||||
|
||||
case GlobalAction.EditorCloneSelection:
|
||||
Clone();
|
||||
return true;
|
||||
|
||||
case GlobalAction.EditorComposeMode:
|
||||
Mode.Value = EditorScreenMode.Compose;
|
||||
return true;
|
||||
@ -741,6 +746,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private EditorMenuItem cutMenuItem;
|
||||
private EditorMenuItem copyMenuItem;
|
||||
private EditorMenuItem cloneMenuItem;
|
||||
private EditorMenuItem pasteMenuItem;
|
||||
|
||||
private readonly BindableWithCurrent<bool> canCut = new BindableWithCurrent<bool>();
|
||||
@ -750,7 +756,11 @@ namespace osu.Game.Screens.Edit
|
||||
private void setUpClipboardActionAvailability()
|
||||
{
|
||||
canCut.Current.BindValueChanged(cut => cutMenuItem.Action.Disabled = !cut.NewValue, true);
|
||||
canCopy.Current.BindValueChanged(copy => copyMenuItem.Action.Disabled = !copy.NewValue, true);
|
||||
canCopy.Current.BindValueChanged(copy =>
|
||||
{
|
||||
copyMenuItem.Action.Disabled = !copy.NewValue;
|
||||
cloneMenuItem.Action.Disabled = !copy.NewValue;
|
||||
}, true);
|
||||
canPaste.Current.BindValueChanged(paste => pasteMenuItem.Action.Disabled = !paste.NewValue, true);
|
||||
}
|
||||
|
||||
@ -765,6 +775,21 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
protected void Copy() => currentScreen?.Copy();
|
||||
|
||||
protected void Clone()
|
||||
{
|
||||
// Avoid attempting to clone if copying is not available (as it may result in pasting something unexpected).
|
||||
if (!canCopy.Value)
|
||||
return;
|
||||
|
||||
// This is an initial implementation just to get an idea of how people used this function.
|
||||
// There are a couple of differences from osu!stable's implementation which will require more work to match:
|
||||
// - The "clipboard" is not populated during the duplication process.
|
||||
// - The duplicated hitobjects are inserted after the original pattern (add one beat_length and then quantize using beat snap).
|
||||
// - The duplicated hitobjects are selected (but this is also applied for all paste operations so should be changed there).
|
||||
Copy();
|
||||
Paste();
|
||||
}
|
||||
|
||||
protected void Paste() => currentScreen?.Paste();
|
||||
|
||||
#endregion
|
||||
|
@ -110,6 +110,8 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
public new void Paste() => base.Paste();
|
||||
|
||||
public new void Clone() => base.Clone();
|
||||
|
||||
public new void SwitchToDifficulty(BeatmapInfo beatmapInfo) => base.SwitchToDifficulty(beatmapInfo);
|
||||
|
||||
public new void CreateNewDifficulty(RulesetInfo rulesetInfo) => base.CreateNewDifficulty(rulesetInfo);
|
||||
|
Loading…
Reference in New Issue
Block a user