1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 09:42:54 +08:00

Updated Implementing a ruleset editor (markdown)

Dean Herbert 2020-05-29 16:02:19 +09:00
parent 8c7de8ea22
commit 0539d9c002

@ -54,25 +54,79 @@ public class TestSceneEditor : EditorTestScene
Running your visual tests, you should now see the editor in a relatively good visual state. You should also notice that basic object movement in the timeline should work as expected, as long as you are doing all `DrawableHitObject` visual update logic in `UpdateState`. This is because `HitObject`'s `StartTime` bindable handling is done for you.
## Making selection and movement work in the compose area
If you are a scrolling ruleset, you will also benefit from selection logic automatically working in the main compose area too.
If not, *todo*
## Keeping DrawableHitObjects compatible with the editor
*todo*
- All transforms and state changes should be done in `UpdateState`. This allows internal logic to update visual state without user intervention.
- DrawablesHitObjects must respond to changes to bindables. Current bindables are:
- StartTimeBindable (handled for the user)
- SamplesBindable
- DurationBindable (coming soon?)
- Any custom attributes added (for instance the `Position` of an `OsuHitObject`) should be bindables and also allow handling the same flow.
## Creating a custom selection handler
*todo*
The default `SelectionHandler` implementation goes a long way to make things work out of the box, but there are some scenarios you will need to create a custom implementation:
### Making selection work for non-scrolling ruleset
If you are a scrolling ruleset, you will also benefit from selection logic automatically working in the main compose area too. If not, you will need to override [`HandleMovement`](https://github.com/ppy/osu/blob/9b277d52e52c926a30e9816393cd734d014c96c4/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs#L74-L86)
### Adding context menu options for current selection
To create context menu items for the current selection, you will need to override [`GetContextMenuItemsForSelection`](https://github.com/ppy/osu/blob/9b277d52e52c926a30e9816393cd734d014c96c4/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs#L291). It is recommended to use `TernaryStateMenuItem`s to correctly represent a selection which has multiple different states, or to hide options which can't feasibly operate on the current selection.
An example of examining and applying the ternary state to the current selection follows:
```csharp
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint> selection)
{
// validity check — we can only show this menu item if all of the selection is the correct type.
if (selection.All(s => s.HitObject is TaikoHitObject))
{
// pre-cast for simplicity.
var hits = selection.Select(s => s.HitObject).OfType<TaikoHitObject>();
yield return new TernaryStateMenuItem("Strong", action: state =>
{
// applied state will always be true or false — this is after a user change.
foreach (var h in hits)
{
switch (state)
{
case TernaryState.True:
h.IsStrong = true;
break;
case TernaryState.False:
h.IsStrong = false;
break;
}
// Only required if you need to run ApplyDefaults on the HitObject.
// If you are handling the change via bindables this is usually not required.
EditorBeatmap?.UpdateHitObject(h);
}
})
{
// set the initial state using a handle helper function below.
State = { Value = getTernaryState(hits, h => h.IsStrong) }
};
}
}
private TernaryState getTernaryState<T>(IEnumerable<T> selection, Func<T, bool> func)
{
if (selection.Any(func))
return selection.All(func) ? TernaryState.True : TernaryState.Indeterminate;
return TernaryState.False;
}
```
## Creating composer blueprints
*todo*
_todo_
## Creating placement tools
*todo*
_todo_