1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-19 09:52:53 +08:00

Add basic skin editor clipboard implementation

This commit is contained in:
Dean Herbert 2023-02-15 19:26:44 +09:00
parent 08ed174f61
commit d653335b6f

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -48,6 +49,9 @@ namespace osu.Game.Overlays.SkinEditor
private Bindable<Skin> currentSkin = null!;
[Cached]
public readonly EditorClipboard Clipboard = new EditorClipboard();
[Resolved]
private OsuGame? game { get; set; }
@ -78,6 +82,15 @@ namespace osu.Game.Overlays.SkinEditor
private EditorMenuItem undoMenuItem = null!;
private EditorMenuItem redoMenuItem = null!;
private EditorMenuItem cutMenuItem = null!;
private EditorMenuItem copyMenuItem = null!;
private EditorMenuItem cloneMenuItem = null!;
private EditorMenuItem pasteMenuItem = null!;
private readonly BindableWithCurrent<bool> canCut = new BindableWithCurrent<bool>();
private readonly BindableWithCurrent<bool> canCopy = new BindableWithCurrent<bool>();
private readonly BindableWithCurrent<bool> canPaste = new BindableWithCurrent<bool>();
[Resolved]
private OnScreenDisplay? onScreenDisplay { get; set; }
@ -143,6 +156,11 @@ namespace osu.Game.Overlays.SkinEditor
{
undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo),
redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo),
new EditorMenuItemSpacer(),
cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut),
copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy),
pasteMenuItem = new EditorMenuItem(CommonStrings.Paste, MenuItemType.Standard, Paste),
cloneMenuItem = new EditorMenuItem(CommonStrings.Clone, MenuItemType.Standard, Clone),
}
},
}
@ -201,6 +219,21 @@ namespace osu.Game.Overlays.SkinEditor
{
base.LoadComplete();
canCut.Current.BindValueChanged(cut => cutMenuItem.Action.Disabled = !cut.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);
SelectedComponents.BindCollectionChanged((_, _) =>
{
canCopy.Value = canCut.Value = SelectedComponents.Any();
}, true);
Clipboard.Content.BindValueChanged(content => canPaste.Value = !string.IsNullOrEmpty(content.NewValue), true);
Show();
game?.RegisterImportHandler(this);
@ -224,6 +257,18 @@ namespace osu.Game.Overlays.SkinEditor
{
switch (e.Action)
{
case PlatformAction.Cut:
Cut();
return true;
case PlatformAction.Copy:
Copy();
return true;
case PlatformAction.Paste:
Paste();
return true;
case PlatformAction.Undo:
Undo();
return true;
@ -361,6 +406,50 @@ namespace osu.Game.Overlays.SkinEditor
}
}
protected void Cut()
{
Copy();
DeleteItems(SelectedComponents.ToArray());
}
protected void Copy()
{
Clipboard.Content.Value = JsonConvert.SerializeObject(SelectedComponents.Cast<Drawable>().Select(s => s.CreateSerialisedInfo()).ToArray());
}
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()
{
var drawableInfo = JsonConvert.DeserializeObject<SerialisedDrawableInfo[]>(Clipboard.Content.Value);
if (drawableInfo == null)
return;
var instances = drawableInfo.Select(d => d.CreateInstance())
.OfType<ISerialisableDrawable>()
.ToArray();
foreach (var i in instances)
placeComponent(i);
SelectedComponents.Clear();
SelectedComponents.AddRange(instances);
}
protected void Undo() => changeHandler?.RestoreState(-1);
protected void Redo() => changeHandler?.RestoreState(1);