mirror of
https://github.com/osukey/osukey.git
synced 2025-05-29 09:27:18 +09:00
Merge pull request #10140 from peppy/editor-clipboard
Editor clipboard support
This commit is contained in:
commit
fc15b4546d
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.910.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.911.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -23,15 +23,19 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestHitObjectAddEvent()
|
public void TestHitObjectAddEvent()
|
||||||
{
|
{
|
||||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
|
||||||
|
|
||||||
HitObject addedObject = null;
|
|
||||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
|
||||||
|
|
||||||
var hitCircle = new HitCircle();
|
var hitCircle = new HitCircle();
|
||||||
|
|
||||||
editorBeatmap.Add(hitCircle);
|
HitObject addedObject = null;
|
||||||
Assert.That(addedObject, Is.EqualTo(hitCircle));
|
EditorBeatmap editorBeatmap = null;
|
||||||
|
|
||||||
|
AddStep("add beatmap", () =>
|
||||||
|
{
|
||||||
|
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
||||||
|
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add hitobject", () => editorBeatmap.Add(hitCircle));
|
||||||
|
AddAssert("received add event", () => addedObject == hitCircle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -41,13 +45,15 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
public void HitObjectRemoveEvent()
|
public void HitObjectRemoveEvent()
|
||||||
{
|
{
|
||||||
var hitCircle = new HitCircle();
|
var hitCircle = new HitCircle();
|
||||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
|
|
||||||
|
|
||||||
HitObject removedObject = null;
|
HitObject removedObject = null;
|
||||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
EditorBeatmap editorBeatmap = null;
|
||||||
|
AddStep("add beatmap", () =>
|
||||||
editorBeatmap.Remove(hitCircle);
|
{
|
||||||
Assert.That(removedObject, Is.EqualTo(hitCircle));
|
Child = editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
|
||||||
|
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||||
|
});
|
||||||
|
AddStep("remove hitobject", () => editorBeatmap.Remove(editorBeatmap.HitObjects.First()));
|
||||||
|
AddAssert("received remove event", () => removedObject == hitCircle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -147,6 +153,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
public void TestResortWhenStartTimeChanged()
|
public void TestResortWhenStartTimeChanged()
|
||||||
{
|
{
|
||||||
var hitCircle = new HitCircle { StartTime = 1000 };
|
var hitCircle = new HitCircle { StartTime = 1000 };
|
||||||
|
|
||||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap
|
var editorBeatmap = new EditorBeatmap(new OsuBeatmap
|
||||||
{
|
{
|
||||||
HitObjects =
|
HitObjects =
|
||||||
|
@ -1,41 +1,27 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
public class TestSceneEditorChangeStates : EditorTestScene
|
public class TestSceneEditorChangeStates : EditorTestScene
|
||||||
{
|
{
|
||||||
private EditorBeatmap editorBeatmap;
|
|
||||||
|
|
||||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
protected new TestEditor Editor => (TestEditor)base.Editor;
|
|
||||||
|
|
||||||
public override void SetUpSteps()
|
|
||||||
{
|
|
||||||
base.SetUpSteps();
|
|
||||||
|
|
||||||
AddStep("get beatmap", () => editorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSelectedObjects()
|
public void TestSelectedObjects()
|
||||||
{
|
{
|
||||||
HitCircle obj = null;
|
HitCircle obj = null;
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(obj = new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(obj = new HitCircle { StartTime = 1000 }));
|
||||||
AddStep("select hitobject", () => editorBeatmap.SelectedHitObjects.Add(obj));
|
AddStep("select hitobject", () => EditorBeatmap.SelectedHitObjects.Add(obj));
|
||||||
AddAssert("confirm 1 selected", () => editorBeatmap.SelectedHitObjects.Count == 1);
|
AddAssert("confirm 1 selected", () => EditorBeatmap.SelectedHitObjects.Count == 1);
|
||||||
AddStep("deselect hitobject", () => editorBeatmap.SelectedHitObjects.Remove(obj));
|
AddStep("deselect hitobject", () => EditorBeatmap.SelectedHitObjects.Remove(obj));
|
||||||
AddAssert("confirm 0 selected", () => editorBeatmap.SelectedHitObjects.Count == 0);
|
AddAssert("confirm 0 selected", () => EditorBeatmap.SelectedHitObjects.Count == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -43,11 +29,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
int hitObjectCount = 0;
|
int hitObjectCount = 0;
|
||||||
|
|
||||||
AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count);
|
AddStep("get initial state", () => hitObjectCount = EditorBeatmap.HitObjects.Count);
|
||||||
|
|
||||||
addUndoSteps();
|
addUndoSteps();
|
||||||
|
|
||||||
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
|
AddAssert("no change occurred", () => hitObjectCount == EditorBeatmap.HitObjects.Count);
|
||||||
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,11 +42,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
int hitObjectCount = 0;
|
int hitObjectCount = 0;
|
||||||
|
|
||||||
AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count);
|
AddStep("get initial state", () => hitObjectCount = EditorBeatmap.HitObjects.Count);
|
||||||
|
|
||||||
addRedoSteps();
|
addRedoSteps();
|
||||||
|
|
||||||
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
|
AddAssert("no change occurred", () => hitObjectCount == EditorBeatmap.HitObjects.Count);
|
||||||
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,11 +59,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("bind removal", () =>
|
AddStep("bind removal", () =>
|
||||||
{
|
{
|
||||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||||
AddAssert("hitobject added", () => addedObject == expectedObject);
|
AddAssert("hitobject added", () => addedObject == expectedObject);
|
||||||
AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
|
AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
|
||||||
|
|
||||||
@ -95,11 +81,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("bind removal", () =>
|
AddStep("bind removal", () =>
|
||||||
{
|
{
|
||||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||||
addUndoSteps();
|
addUndoSteps();
|
||||||
|
|
||||||
AddStep("reset variables", () =>
|
AddStep("reset variables", () =>
|
||||||
@ -117,7 +103,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAddObjectThenSaveHasNoUnsavedChanges()
|
public void TestAddObjectThenSaveHasNoUnsavedChanges()
|
||||||
{
|
{
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(new HitCircle { StartTime = 1000 }));
|
||||||
|
|
||||||
AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
|
AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
|
||||||
AddStep("save changes", () => Editor.Save());
|
AddStep("save changes", () => Editor.Save());
|
||||||
@ -133,12 +119,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("bind removal", () =>
|
AddStep("bind removal", () =>
|
||||||
{
|
{
|
||||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||||
AddStep("remove object", () => editorBeatmap.Remove(expectedObject));
|
AddStep("remove object", () => EditorBeatmap.Remove(expectedObject));
|
||||||
AddStep("reset variables", () =>
|
AddStep("reset variables", () =>
|
||||||
{
|
{
|
||||||
addedObject = null;
|
addedObject = null;
|
||||||
@ -160,12 +146,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("bind removal", () =>
|
AddStep("bind removal", () =>
|
||||||
{
|
{
|
||||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||||
AddStep("remove object", () => editorBeatmap.Remove(expectedObject));
|
AddStep("remove object", () => EditorBeatmap.Remove(expectedObject));
|
||||||
addUndoSteps();
|
addUndoSteps();
|
||||||
|
|
||||||
AddStep("reset variables", () =>
|
AddStep("reset variables", () =>
|
||||||
@ -183,18 +169,5 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void addUndoSteps() => AddStep("undo", () => Editor.Undo());
|
private void addUndoSteps() => AddStep("undo", () => Editor.Undo());
|
||||||
|
|
||||||
private void addRedoSteps() => AddStep("redo", () => Editor.Redo());
|
private void addRedoSteps() => AddStep("redo", () => Editor.Redo());
|
||||||
|
|
||||||
protected override Editor CreateEditor() => new TestEditor();
|
|
||||||
|
|
||||||
protected class TestEditor : Editor
|
|
||||||
{
|
|
||||||
public new void Undo() => base.Undo();
|
|
||||||
|
|
||||||
public new void Redo() => base.Redo();
|
|
||||||
|
|
||||||
public new void Save() => base.Save();
|
|
||||||
|
|
||||||
public new bool HasUnsavedChanges => base.HasUnsavedChanges;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
154
osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs
Normal file
154
osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// 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.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneEditorClipboard : EditorTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCutRemovesObjects()
|
||||||
|
{
|
||||||
|
var addedObject = new HitCircle { StartTime = 1000 };
|
||||||
|
|
||||||
|
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("cut hitobject", () => Editor.Cut());
|
||||||
|
|
||||||
|
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(1000)]
|
||||||
|
[TestCase(2000)]
|
||||||
|
public void TestCutPaste(double newTime)
|
||||||
|
{
|
||||||
|
var addedObject = new HitCircle { StartTime = 1000 };
|
||||||
|
|
||||||
|
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("cut hitobject", () => Editor.Cut());
|
||||||
|
|
||||||
|
AddStep("move forward in time", () => EditorClock.Seek(newTime));
|
||||||
|
|
||||||
|
AddStep("paste hitobject", () => Editor.Paste());
|
||||||
|
|
||||||
|
AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||||
|
|
||||||
|
AddAssert("new object selected", () => EditorBeatmap.SelectedHitObjects.Single().StartTime == newTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCutPasteSlider()
|
||||||
|
{
|
||||||
|
var addedObject = new Slider
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Path = new SliderPath
|
||||||
|
{
|
||||||
|
ControlPoints =
|
||||||
|
{
|
||||||
|
new PathControlPoint(),
|
||||||
|
new PathControlPoint(new Vector2(100, 0), PathType.Bezier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("cut hitobject", () => Editor.Cut());
|
||||||
|
|
||||||
|
AddStep("paste hitobject", () => Editor.Paste());
|
||||||
|
|
||||||
|
AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||||
|
|
||||||
|
AddAssert("path matches", () =>
|
||||||
|
{
|
||||||
|
var path = ((Slider)EditorBeatmap.HitObjects.Single()).Path;
|
||||||
|
return path.ControlPoints.Count == 2 && path.ControlPoints.SequenceEqual(addedObject.Path.ControlPoints);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCutPasteSpinner()
|
||||||
|
{
|
||||||
|
var addedObject = new Spinner
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Duration = 5000
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||||
|
|
||||||
|
AddStep("cut hitobject", () => Editor.Cut());
|
||||||
|
|
||||||
|
AddStep("paste hitobject", () => Editor.Paste());
|
||||||
|
|
||||||
|
AddAssert("is one object", () => EditorBeatmap.HitObjects.Count == 1);
|
||||||
|
|
||||||
|
AddAssert("duration matches", () => ((Spinner)EditorBeatmap.HitObjects.Single()).Duration == 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCopyPaste()
|
||||||
|
{
|
||||||
|
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("move forward in time", () => EditorClock.Seek(2000));
|
||||||
|
|
||||||
|
AddStep("paste hitobject", () => Editor.Paste());
|
||||||
|
|
||||||
|
AddAssert("are two objects", () => EditorBeatmap.HitObjects.Count == 2);
|
||||||
|
|
||||||
|
AddAssert("new object selected", () => EditorBeatmap.SelectedHitObjects.Single().StartTime == 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCutNothing()
|
||||||
|
{
|
||||||
|
AddStep("cut hitobject", () => Editor.Cut());
|
||||||
|
AddAssert("are no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCopyNothing()
|
||||||
|
{
|
||||||
|
AddStep("copy hitobject", () => Editor.Copy());
|
||||||
|
AddAssert("are no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPasteNothing()
|
||||||
|
{
|
||||||
|
AddStep("paste hitobject", () => Editor.Paste());
|
||||||
|
AddAssert("are no objects", () => EditorBeatmap.HitObjects.Count == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
osu.Game/Screens/Edit/ClipboardContent.cs
Normal file
27
osu.Game/Screens/Edit/ClipboardContent.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Game.IO.Serialization;
|
||||||
|
using osu.Game.IO.Serialization.Converters;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public class ClipboardContent : IJsonSerializable
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
||||||
|
public IList<HitObject> HitObjects;
|
||||||
|
|
||||||
|
public ClipboardContent()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClipboardContent(EditorBeatmap editorBeatmap)
|
||||||
|
{
|
||||||
|
HitObjects = editorBeatmap.SelectedHitObjects.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -271,6 +271,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
blueprint.Selected += onBlueprintSelected;
|
blueprint.Selected += onBlueprintSelected;
|
||||||
blueprint.Deselected += onBlueprintDeselected;
|
blueprint.Deselected += onBlueprintDeselected;
|
||||||
|
|
||||||
|
if (beatmap.SelectedHitObjects.Contains(hitObject))
|
||||||
|
blueprint.Select();
|
||||||
|
|
||||||
SelectionBlueprints.Add(blueprint);
|
SelectionBlueprints.Add(blueprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -22,6 +24,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -131,9 +134,14 @@ namespace osu.Game.Screens.Edit
|
|||||||
updateLastSavedHash();
|
updateLastSavedHash();
|
||||||
|
|
||||||
EditorMenuBar menuBar;
|
EditorMenuBar menuBar;
|
||||||
|
|
||||||
OsuMenuItem undoMenuItem;
|
OsuMenuItem undoMenuItem;
|
||||||
OsuMenuItem redoMenuItem;
|
OsuMenuItem redoMenuItem;
|
||||||
|
|
||||||
|
EditorMenuItem cutMenuItem;
|
||||||
|
EditorMenuItem copyMenuItem;
|
||||||
|
EditorMenuItem pasteMenuItem;
|
||||||
|
|
||||||
var fileMenuItems = new List<MenuItem>
|
var fileMenuItems = new List<MenuItem>
|
||||||
{
|
{
|
||||||
new EditorMenuItem("Save", MenuItemType.Standard, Save)
|
new EditorMenuItem("Save", MenuItemType.Standard, Save)
|
||||||
@ -183,7 +191,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
Items = new[]
|
Items = new[]
|
||||||
{
|
{
|
||||||
undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo),
|
undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo),
|
||||||
redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo)
|
redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo),
|
||||||
|
new EditorMenuItemSpacer(),
|
||||||
|
cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut),
|
||||||
|
copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy),
|
||||||
|
pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,6 +256,16 @@ namespace osu.Game.Screens.Edit
|
|||||||
changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
|
|
||||||
|
editorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) =>
|
||||||
|
{
|
||||||
|
var hasObjects = editorBeatmap.SelectedHitObjects.Count > 0;
|
||||||
|
|
||||||
|
cutMenuItem.Action.Disabled = !hasObjects;
|
||||||
|
copyMenuItem.Action.Disabled = !hasObjects;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue));
|
||||||
|
|
||||||
menuBar.Mode.ValueChanged += onModeChanged;
|
menuBar.Mode.ValueChanged += onModeChanged;
|
||||||
|
|
||||||
bottomBackground.Colour = colours.Gray2;
|
bottomBackground.Colour = colours.Gray2;
|
||||||
@ -270,6 +292,18 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
switch (action.ActionType)
|
switch (action.ActionType)
|
||||||
{
|
{
|
||||||
|
case PlatformActionType.Cut:
|
||||||
|
Cut();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PlatformActionType.Copy:
|
||||||
|
Copy();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PlatformActionType.Paste:
|
||||||
|
Paste();
|
||||||
|
return true;
|
||||||
|
|
||||||
case PlatformActionType.Undo:
|
case PlatformActionType.Undo:
|
||||||
Undo();
|
Undo();
|
||||||
return true;
|
return true;
|
||||||
@ -394,6 +428,47 @@ namespace osu.Game.Screens.Edit
|
|||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Bindable<string> clipboard = new Bindable<string>();
|
||||||
|
|
||||||
|
protected void Cut()
|
||||||
|
{
|
||||||
|
Copy();
|
||||||
|
foreach (var h in editorBeatmap.SelectedHitObjects.ToArray())
|
||||||
|
editorBeatmap.Remove(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Copy()
|
||||||
|
{
|
||||||
|
if (editorBeatmap.SelectedHitObjects.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clipboard.Value = new ClipboardContent(editorBeatmap).Serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Paste()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(clipboard.Value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var objects = clipboard.Value.Deserialize<ClipboardContent>().HitObjects;
|
||||||
|
|
||||||
|
Debug.Assert(objects.Any());
|
||||||
|
|
||||||
|
double timeOffset = clock.CurrentTime - objects.Min(o => o.StartTime);
|
||||||
|
|
||||||
|
foreach (var h in objects)
|
||||||
|
h.StartTime += timeOffset;
|
||||||
|
|
||||||
|
changeHandler.BeginChange();
|
||||||
|
|
||||||
|
editorBeatmap.SelectedHitObjects.Clear();
|
||||||
|
|
||||||
|
editorBeatmap.AddRange(objects);
|
||||||
|
editorBeatmap.SelectedHitObjects.AddRange(objects);
|
||||||
|
|
||||||
|
changeHandler.EndChange();
|
||||||
|
}
|
||||||
|
|
||||||
protected void Undo() => changeHandler.RestoreState(-1);
|
protected void Undo() => changeHandler.RestoreState(-1);
|
||||||
|
|
||||||
protected void Redo() => changeHandler.RestoreState(1);
|
protected void Redo() => changeHandler.RestoreState(1);
|
||||||
|
@ -9,7 +9,6 @@ using JetBrains.Annotations;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
@ -68,41 +67,6 @@ namespace osu.Game.Screens.Edit
|
|||||||
trackStartTime(obj);
|
trackStartTime(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly HashSet<HitObject> pendingUpdates = new HashSet<HitObject>();
|
|
||||||
private ScheduledDelegate scheduledUpdate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates a <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> to update.</param>
|
|
||||||
public void UpdateHitObject([NotNull] HitObject hitObject) => updateHitObject(hitObject, false);
|
|
||||||
|
|
||||||
private void updateHitObject([CanBeNull] HitObject hitObject, bool silent)
|
|
||||||
{
|
|
||||||
scheduledUpdate?.Cancel();
|
|
||||||
|
|
||||||
if (hitObject != null)
|
|
||||||
pendingUpdates.Add(hitObject);
|
|
||||||
|
|
||||||
scheduledUpdate = Schedule(() =>
|
|
||||||
{
|
|
||||||
beatmapProcessor?.PreProcess();
|
|
||||||
|
|
||||||
foreach (var obj in pendingUpdates)
|
|
||||||
obj.ApplyDefaults(ControlPointInfo, BeatmapInfo.BaseDifficulty);
|
|
||||||
|
|
||||||
beatmapProcessor?.PostProcess();
|
|
||||||
|
|
||||||
if (!silent)
|
|
||||||
{
|
|
||||||
foreach (var obj in pendingUpdates)
|
|
||||||
HitObjectUpdated?.Invoke(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingUpdates.Clear();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeatmapInfo BeatmapInfo
|
public BeatmapInfo BeatmapInfo
|
||||||
{
|
{
|
||||||
get => PlayableBeatmap.BeatmapInfo;
|
get => PlayableBeatmap.BeatmapInfo;
|
||||||
@ -125,6 +89,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
|
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
|
||||||
|
|
||||||
|
private readonly HashSet<HitObject> pendingUpdates = new HashSet<HitObject>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a collection of <see cref="HitObject"/>s to this <see cref="EditorBeatmap"/>.
|
/// Adds a collection of <see cref="HitObject"/>s to this <see cref="EditorBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -160,14 +126,27 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
mutableHitObjects.Insert(index, hitObject);
|
mutableHitObjects.Insert(index, hitObject);
|
||||||
|
|
||||||
|
// must be run after any change to hitobject ordering
|
||||||
|
beatmapProcessor?.PreProcess();
|
||||||
|
processHitObject(hitObject);
|
||||||
|
beatmapProcessor?.PostProcess();
|
||||||
|
|
||||||
HitObjectAdded?.Invoke(hitObject);
|
HitObjectAdded?.Invoke(hitObject);
|
||||||
updateHitObject(hitObject, true);
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> to update.</param>
|
||||||
|
public void UpdateHitObject([NotNull] HitObject hitObject)
|
||||||
|
{
|
||||||
|
pendingUpdates.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a <see cref="HitObject"/> from this <see cref="EditorBeatmap"/>.
|
/// Removes a <see cref="HitObject"/> from this <see cref="EditorBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
|
/// <param name="hitObject">The <see cref="HitObject"/> to remove.</param>
|
||||||
/// <returns>True if the <see cref="HitObject"/> has been removed, false otherwise.</returns>
|
/// <returns>True if the <see cref="HitObject"/> has been removed, false otherwise.</returns>
|
||||||
public bool Remove(HitObject hitObject)
|
public bool Remove(HitObject hitObject)
|
||||||
{
|
{
|
||||||
@ -199,11 +178,14 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
var bindable = startTimeBindables[hitObject];
|
var bindable = startTimeBindables[hitObject];
|
||||||
bindable.UnbindAll();
|
bindable.UnbindAll();
|
||||||
|
|
||||||
startTimeBindables.Remove(hitObject);
|
startTimeBindables.Remove(hitObject);
|
||||||
HitObjectRemoved?.Invoke(hitObject);
|
|
||||||
|
|
||||||
updateHitObject(null, true);
|
// must be run after any change to hitobject ordering
|
||||||
|
beatmapProcessor?.PreProcess();
|
||||||
|
processHitObject(hitObject);
|
||||||
|
beatmapProcessor?.PostProcess();
|
||||||
|
|
||||||
|
HitObjectRemoved?.Invoke(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -211,20 +193,33 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
var removed = HitObjects.ToList();
|
foreach (var h in HitObjects.ToArray())
|
||||||
|
Remove(h);
|
||||||
mutableHitObjects.Clear();
|
|
||||||
|
|
||||||
foreach (var b in startTimeBindables)
|
|
||||||
b.Value.UnbindAll();
|
|
||||||
startTimeBindables.Clear();
|
|
||||||
|
|
||||||
foreach (var h in removed)
|
|
||||||
HitObjectRemoved?.Invoke(h);
|
|
||||||
|
|
||||||
updateHitObject(null, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// debounce updates as they are common and may come from input events, which can run needlessly many times per update frame.
|
||||||
|
if (pendingUpdates.Count > 0)
|
||||||
|
{
|
||||||
|
beatmapProcessor?.PreProcess();
|
||||||
|
|
||||||
|
foreach (var hitObject in pendingUpdates)
|
||||||
|
{
|
||||||
|
processHitObject(hitObject);
|
||||||
|
HitObjectUpdated?.Invoke(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingUpdates.Clear();
|
||||||
|
|
||||||
|
beatmapProcessor?.PostProcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processHitObject(HitObject hitObject) => hitObject.ApplyDefaults(ControlPointInfo, BeatmapInfo.BaseDifficulty);
|
||||||
|
|
||||||
private void trackStartTime(HitObject hitObject)
|
private void trackStartTime(HitObject hitObject)
|
||||||
{
|
{
|
||||||
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();
|
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();
|
||||||
|
@ -15,14 +15,16 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
{
|
{
|
||||||
public class TestBeatmap : Beatmap
|
public class TestBeatmap : Beatmap
|
||||||
{
|
{
|
||||||
public TestBeatmap(RulesetInfo ruleset)
|
public TestBeatmap(RulesetInfo ruleset, bool withHitObjects = true)
|
||||||
{
|
{
|
||||||
var baseBeatmap = CreateBeatmap();
|
var baseBeatmap = CreateBeatmap();
|
||||||
|
|
||||||
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
||||||
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
||||||
Breaks = baseBeatmap.Breaks;
|
Breaks = baseBeatmap.Breaks;
|
||||||
HitObjects = baseBeatmap.HitObjects;
|
|
||||||
|
if (withHitObjects)
|
||||||
|
HitObjects = baseBeatmap.HitObjects;
|
||||||
|
|
||||||
BeatmapInfo.Ruleset = ruleset;
|
BeatmapInfo.Ruleset = ruleset;
|
||||||
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
|
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
|
||||||
|
@ -14,7 +14,11 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
public abstract class EditorTestScene : ScreenTestScene
|
public abstract class EditorTestScene : ScreenTestScene
|
||||||
{
|
{
|
||||||
protected Editor Editor { get; private set; }
|
protected EditorBeatmap EditorBeatmap;
|
||||||
|
|
||||||
|
protected TestEditor Editor { get; private set; }
|
||||||
|
|
||||||
|
protected EditorClock EditorClock { get; private set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -29,6 +33,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
||||||
AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
||||||
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
|
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
|
||||||
|
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
||||||
|
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -39,6 +45,23 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
protected sealed override Ruleset CreateRuleset() => CreateEditorRuleset();
|
protected sealed override Ruleset CreateRuleset() => CreateEditorRuleset();
|
||||||
|
|
||||||
protected virtual Editor CreateEditor() => new Editor();
|
protected virtual TestEditor CreateEditor() => new TestEditor();
|
||||||
|
|
||||||
|
protected class TestEditor : Editor
|
||||||
|
{
|
||||||
|
public new void Undo() => base.Undo();
|
||||||
|
|
||||||
|
public new void Redo() => base.Redo();
|
||||||
|
|
||||||
|
public new void Save() => base.Save();
|
||||||
|
|
||||||
|
public new void Cut() => base.Cut();
|
||||||
|
|
||||||
|
public new void Copy() => base.Copy();
|
||||||
|
|
||||||
|
public new void Paste() => base.Paste();
|
||||||
|
|
||||||
|
public new bool HasUnsavedChanges => base.HasUnsavedChanges;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.910.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.911.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
<PackageReference Include="Sentry" Version="2.1.6" />
|
<PackageReference Include="Sentry" Version="2.1.6" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.910.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.911.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.910.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.911.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user