mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 06:57:39 +08:00
Merge branch 'master' into fix-test-storage-weirdness
This commit is contained in:
commit
8f5115b4e8
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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>
|
||||
</Project>
|
||||
|
@ -23,15 +23,19 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestHitObjectAddEvent()
|
||||
{
|
||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
||||
|
||||
HitObject addedObject = null;
|
||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
|
||||
var hitCircle = new HitCircle();
|
||||
|
||||
editorBeatmap.Add(hitCircle);
|
||||
Assert.That(addedObject, Is.EqualTo(hitCircle));
|
||||
HitObject addedObject = null;
|
||||
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>
|
||||
@ -41,13 +45,15 @@ namespace osu.Game.Tests.Beatmaps
|
||||
public void HitObjectRemoveEvent()
|
||||
{
|
||||
var hitCircle = new HitCircle();
|
||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap { HitObjects = { hitCircle } });
|
||||
|
||||
HitObject removedObject = null;
|
||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
|
||||
editorBeatmap.Remove(hitCircle);
|
||||
Assert.That(removedObject, Is.EqualTo(hitCircle));
|
||||
EditorBeatmap editorBeatmap = null;
|
||||
AddStep("add beatmap", () =>
|
||||
{
|
||||
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>
|
||||
@ -147,6 +153,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
public void TestResortWhenStartTimeChanged()
|
||||
{
|
||||
var hitCircle = new HitCircle { StartTime = 1000 };
|
||||
|
||||
var editorBeatmap = new EditorBeatmap(new OsuBeatmap
|
||||
{
|
||||
HitObjects =
|
||||
|
@ -1,41 +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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorChangeStates : EditorTestScene
|
||||
{
|
||||
private EditorBeatmap editorBeatmap;
|
||||
|
||||
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]
|
||||
public void TestSelectedObjects()
|
||||
{
|
||||
HitCircle obj = null;
|
||||
AddStep("add hitobject", () => editorBeatmap.Add(obj = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("select hitobject", () => editorBeatmap.SelectedHitObjects.Add(obj));
|
||||
AddAssert("confirm 1 selected", () => editorBeatmap.SelectedHitObjects.Count == 1);
|
||||
AddStep("deselect hitobject", () => editorBeatmap.SelectedHitObjects.Remove(obj));
|
||||
AddAssert("confirm 0 selected", () => editorBeatmap.SelectedHitObjects.Count == 0);
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(obj = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("select hitobject", () => EditorBeatmap.SelectedHitObjects.Add(obj));
|
||||
AddAssert("confirm 1 selected", () => EditorBeatmap.SelectedHitObjects.Count == 1);
|
||||
AddStep("deselect hitobject", () => EditorBeatmap.SelectedHitObjects.Remove(obj));
|
||||
AddAssert("confirm 0 selected", () => EditorBeatmap.SelectedHitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -43,11 +29,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
int hitObjectCount = 0;
|
||||
|
||||
AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count);
|
||||
AddStep("get initial state", () => hitObjectCount = EditorBeatmap.HitObjects.Count);
|
||||
|
||||
addUndoSteps();
|
||||
|
||||
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
|
||||
AddAssert("no change occurred", () => hitObjectCount == EditorBeatmap.HitObjects.Count);
|
||||
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
||||
}
|
||||
|
||||
@ -56,11 +42,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
int hitObjectCount = 0;
|
||||
|
||||
AddStep("get initial state", () => hitObjectCount = editorBeatmap.HitObjects.Count);
|
||||
AddStep("get initial state", () => hitObjectCount = EditorBeatmap.HitObjects.Count);
|
||||
|
||||
addRedoSteps();
|
||||
|
||||
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
|
||||
AddAssert("no change occurred", () => hitObjectCount == EditorBeatmap.HitObjects.Count);
|
||||
AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
|
||||
}
|
||||
|
||||
@ -73,11 +59,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("bind removal", () =>
|
||||
{
|
||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
EditorBeatmap.HitObjectAdded += h => addedObject = 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("unsaved changes", () => Editor.HasUnsavedChanges);
|
||||
|
||||
@ -95,11 +81,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("bind removal", () =>
|
||||
{
|
||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
EditorBeatmap.HitObjectAdded += h => addedObject = 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();
|
||||
|
||||
AddStep("reset variables", () =>
|
||||
@ -117,7 +103,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
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);
|
||||
AddStep("save changes", () => Editor.Save());
|
||||
@ -133,12 +119,12 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("bind removal", () =>
|
||||
{
|
||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
});
|
||||
|
||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("remove object", () => editorBeatmap.Remove(expectedObject));
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("remove object", () => EditorBeatmap.Remove(expectedObject));
|
||||
AddStep("reset variables", () =>
|
||||
{
|
||||
addedObject = null;
|
||||
@ -160,12 +146,12 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("bind removal", () =>
|
||||
{
|
||||
editorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
editorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
EditorBeatmap.HitObjectAdded += h => addedObject = h;
|
||||
EditorBeatmap.HitObjectRemoved += h => removedObject = h;
|
||||
});
|
||||
|
||||
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("remove object", () => editorBeatmap.Remove(expectedObject));
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
|
||||
AddStep("remove object", () => EditorBeatmap.Remove(expectedObject));
|
||||
addUndoSteps();
|
||||
|
||||
AddStep("reset variables", () =>
|
||||
@ -183,18 +169,5 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private void addUndoSteps() => AddStep("undo", () => Editor.Undo());
|
||||
|
||||
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.Deselected += onBlueprintDeselected;
|
||||
|
||||
if (beatmap.SelectedHitObjects.Contains(hitObject))
|
||||
blueprint.Select();
|
||||
|
||||
SelectionBlueprints.Add(blueprint);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -22,6 +24,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -131,9 +134,14 @@ namespace osu.Game.Screens.Edit
|
||||
updateLastSavedHash();
|
||||
|
||||
EditorMenuBar menuBar;
|
||||
|
||||
OsuMenuItem undoMenuItem;
|
||||
OsuMenuItem redoMenuItem;
|
||||
|
||||
EditorMenuItem cutMenuItem;
|
||||
EditorMenuItem copyMenuItem;
|
||||
EditorMenuItem pasteMenuItem;
|
||||
|
||||
var fileMenuItems = new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem("Save", MenuItemType.Standard, Save)
|
||||
@ -183,7 +191,11 @@ namespace osu.Game.Screens.Edit
|
||||
Items = new[]
|
||||
{
|
||||
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.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;
|
||||
|
||||
bottomBackground.Colour = colours.Gray2;
|
||||
@ -270,6 +292,18 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
switch (action.ActionType)
|
||||
{
|
||||
case PlatformActionType.Cut:
|
||||
Cut();
|
||||
return true;
|
||||
|
||||
case PlatformActionType.Copy:
|
||||
Copy();
|
||||
return true;
|
||||
|
||||
case PlatformActionType.Paste:
|
||||
Paste();
|
||||
return true;
|
||||
|
||||
case PlatformActionType.Undo:
|
||||
Undo();
|
||||
return true;
|
||||
@ -394,6 +428,47 @@ namespace osu.Game.Screens.Edit
|
||||
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 Redo() => changeHandler.RestoreState(1);
|
||||
|
@ -9,7 +9,6 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
@ -68,41 +67,6 @@ namespace osu.Game.Screens.Edit
|
||||
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
|
||||
{
|
||||
get => PlayableBeatmap.BeatmapInfo;
|
||||
@ -125,6 +89,8 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private IList mutableHitObjects => (IList)PlayableBeatmap.HitObjects;
|
||||
|
||||
private readonly HashSet<HitObject> pendingUpdates = new HashSet<HitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of <see cref="HitObject"/>s to this <see cref="EditorBeatmap"/>.
|
||||
/// </summary>
|
||||
@ -160,14 +126,27 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
mutableHitObjects.Insert(index, hitObject);
|
||||
|
||||
// must be run after any change to hitobject ordering
|
||||
beatmapProcessor?.PreProcess();
|
||||
processHitObject(hitObject);
|
||||
beatmapProcessor?.PostProcess();
|
||||
|
||||
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>
|
||||
/// Removes a <see cref="HitObject"/> from this <see cref="EditorBeatmap"/>.
|
||||
/// </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>
|
||||
public bool Remove(HitObject hitObject)
|
||||
{
|
||||
@ -199,11 +178,14 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
var bindable = startTimeBindables[hitObject];
|
||||
bindable.UnbindAll();
|
||||
|
||||
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>
|
||||
@ -211,20 +193,33 @@ namespace osu.Game.Screens.Edit
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
var removed = HitObjects.ToList();
|
||||
|
||||
mutableHitObjects.Clear();
|
||||
|
||||
foreach (var b in startTimeBindables)
|
||||
b.Value.UnbindAll();
|
||||
startTimeBindables.Clear();
|
||||
|
||||
foreach (var h in removed)
|
||||
HitObjectRemoved?.Invoke(h);
|
||||
|
||||
updateHitObject(null, true);
|
||||
foreach (var h in HitObjects.ToArray())
|
||||
Remove(h);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();
|
||||
|
@ -15,14 +15,16 @@ namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
public class TestBeatmap : Beatmap
|
||||
{
|
||||
public TestBeatmap(RulesetInfo ruleset)
|
||||
public TestBeatmap(RulesetInfo ruleset, bool withHitObjects = true)
|
||||
{
|
||||
var baseBeatmap = CreateBeatmap();
|
||||
|
||||
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
||||
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
||||
Breaks = baseBeatmap.Breaks;
|
||||
HitObjects = baseBeatmap.HitObjects;
|
||||
|
||||
if (withHitObjects)
|
||||
HitObjects = baseBeatmap.HitObjects;
|
||||
|
||||
BeatmapInfo.Ruleset = ruleset;
|
||||
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
|
||||
|
@ -14,7 +14,11 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
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]
|
||||
private void load()
|
||||
@ -29,6 +33,8 @@ namespace osu.Game.Tests.Visual
|
||||
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
||||
AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().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>
|
||||
@ -39,6 +45,23 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
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.Core" Version="2.2.6" />
|
||||
<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="Sentry" Version="2.1.6" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<!-- 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.Core" Version="2.2.6" />
|
||||
<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="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user