2021-04-28 14:14:48 +08:00
|
|
|
// 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.
|
|
|
|
|
2021-05-11 16:49:00 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.Specialized;
|
2022-12-16 19:18:02 +08:00
|
|
|
using System.Diagnostics;
|
2021-04-28 14:14:48 +08:00
|
|
|
using System.Linq;
|
2021-05-11 16:49:00 +08:00
|
|
|
using osu.Framework.Allocation;
|
|
|
|
using osu.Framework.Bindables;
|
2021-04-28 14:14:48 +08:00
|
|
|
using osu.Framework.Graphics;
|
2021-05-11 17:37:41 +08:00
|
|
|
using osu.Framework.Screens;
|
2021-04-28 14:14:48 +08:00
|
|
|
using osu.Framework.Testing;
|
2022-09-08 23:14:34 +08:00
|
|
|
using osu.Framework.Input.Events;
|
2021-04-28 14:14:48 +08:00
|
|
|
using osu.Game.Rulesets.Edit;
|
2021-05-11 17:37:41 +08:00
|
|
|
using osu.Game.Screens;
|
2021-04-28 14:14:48 +08:00
|
|
|
using osu.Game.Screens.Edit.Compose.Components;
|
2022-09-08 23:14:34 +08:00
|
|
|
using osuTK;
|
|
|
|
using osuTK.Input;
|
2021-04-28 14:14:48 +08:00
|
|
|
|
|
|
|
namespace osu.Game.Skinning.Editor
|
|
|
|
{
|
2021-05-13 16:06:00 +08:00
|
|
|
public partial class SkinBlueprintContainer : BlueprintContainer<ISkinnableDrawable>
|
2021-04-28 14:14:48 +08:00
|
|
|
{
|
|
|
|
private readonly Drawable target;
|
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
private readonly List<BindableList<ISkinnableDrawable>> targetComponents = new List<BindableList<ISkinnableDrawable>>();
|
2021-05-13 12:03:23 +08:00
|
|
|
|
2022-04-07 18:12:10 +08:00
|
|
|
[Resolved]
|
2023-01-26 16:46:19 +08:00
|
|
|
private SkinEditor editor { get; set; } = null!;
|
2022-04-07 18:12:10 +08:00
|
|
|
|
2021-04-28 14:14:48 +08:00
|
|
|
public SkinBlueprintContainer(Drawable target)
|
|
|
|
{
|
|
|
|
this.target = target;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
2022-04-07 18:12:10 +08:00
|
|
|
SelectedItems.BindTo(editor.SelectedComponents);
|
|
|
|
|
2021-05-11 16:49:00 +08:00
|
|
|
// track each target container on the current screen.
|
2021-05-12 14:58:21 +08:00
|
|
|
var targetContainers = target.ChildrenOfType<ISkinnableTarget>().ToArray();
|
2021-05-11 17:37:41 +08:00
|
|
|
|
|
|
|
if (targetContainers.Length == 0)
|
|
|
|
{
|
2021-10-27 12:04:41 +08:00
|
|
|
string targetScreen = target.ChildrenOfType<Screen>().LastOrDefault()?.GetType().Name ?? "this screen";
|
2021-05-11 17:37:41 +08:00
|
|
|
|
|
|
|
AddInternal(new ScreenWhiteBox.UnderConstructionMessage(targetScreen, "doesn't support skin customisation just yet."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var targetContainer in targetContainers)
|
2021-05-11 16:49:00 +08:00
|
|
|
{
|
2021-05-13 16:06:00 +08:00
|
|
|
var bindableList = new BindableList<ISkinnableDrawable> { BindTarget = targetContainer.Components };
|
2021-05-11 16:49:00 +08:00
|
|
|
bindableList.BindCollectionChanged(componentsChanged, true);
|
|
|
|
|
|
|
|
targetComponents.Add(bindableList);
|
|
|
|
}
|
2021-04-28 17:59:55 +08:00
|
|
|
}
|
|
|
|
|
2023-01-26 16:46:19 +08:00
|
|
|
private void componentsChanged(object? sender, NotifyCollectionChangedEventArgs e) => Schedule(() =>
|
2021-04-28 17:59:55 +08:00
|
|
|
{
|
2021-05-11 16:49:00 +08:00
|
|
|
switch (e.Action)
|
|
|
|
{
|
|
|
|
case NotifyCollectionChangedAction.Add:
|
2022-12-16 19:18:02 +08:00
|
|
|
Debug.Assert(e.NewItems != null);
|
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
foreach (var item in e.NewItems.Cast<ISkinnableDrawable>())
|
2021-05-11 16:49:00 +08:00
|
|
|
AddBlueprintFor(item);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
|
|
|
case NotifyCollectionChangedAction.Reset:
|
2022-12-16 19:18:02 +08:00
|
|
|
Debug.Assert(e.OldItems != null);
|
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
foreach (var item in e.OldItems.Cast<ISkinnableDrawable>())
|
2021-05-11 16:49:00 +08:00
|
|
|
RemoveBlueprintFor(item);
|
|
|
|
break;
|
2021-05-06 14:16:16 +08:00
|
|
|
|
2021-05-11 16:49:00 +08:00
|
|
|
case NotifyCollectionChangedAction.Replace:
|
2022-12-16 19:18:02 +08:00
|
|
|
Debug.Assert(e.NewItems != null);
|
|
|
|
Debug.Assert(e.OldItems != null);
|
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
foreach (var item in e.OldItems.Cast<ISkinnableDrawable>())
|
2021-05-11 16:49:00 +08:00
|
|
|
RemoveBlueprintFor(item);
|
2021-04-28 14:14:48 +08:00
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
foreach (var item in e.NewItems.Cast<ISkinnableDrawable>())
|
2021-05-11 16:49:00 +08:00
|
|
|
AddBlueprintFor(item);
|
|
|
|
break;
|
|
|
|
}
|
2022-04-07 18:12:10 +08:00
|
|
|
});
|
2021-04-28 14:14:48 +08:00
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
protected override void AddBlueprintFor(ISkinnableDrawable item)
|
2021-05-13 16:03:17 +08:00
|
|
|
{
|
|
|
|
if (!item.IsEditable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
base.AddBlueprintFor(item);
|
|
|
|
}
|
|
|
|
|
2022-09-08 23:14:34 +08:00
|
|
|
protected override bool OnKeyDown(KeyDownEvent e)
|
|
|
|
{
|
|
|
|
switch (e.Key)
|
|
|
|
{
|
|
|
|
case Key.Left:
|
|
|
|
moveSelection(new Vector2(-1, 0));
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Key.Right:
|
|
|
|
moveSelection(new Vector2(1, 0));
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Key.Up:
|
|
|
|
moveSelection(new Vector2(0, -1));
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case Key.Down:
|
|
|
|
moveSelection(new Vector2(0, 1));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-06 12:50:56 +08:00
|
|
|
protected override void SelectAll()
|
|
|
|
{
|
2022-10-07 09:46:07 +08:00
|
|
|
SelectedItems.AddRange(targetComponents.SelectMany(list => list).Except(SelectedItems).ToArray());
|
2022-10-06 12:50:56 +08:00
|
|
|
}
|
|
|
|
|
2022-09-08 23:14:34 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Move the current selection spatially by the specified delta, in screen coordinates (ie. the same coordinates as the blueprints).
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="delta"></param>
|
|
|
|
private void moveSelection(Vector2 delta)
|
|
|
|
{
|
|
|
|
var firstBlueprint = SelectionHandler.SelectedBlueprints.FirstOrDefault();
|
|
|
|
|
|
|
|
if (firstBlueprint == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// convert to game space coordinates
|
|
|
|
delta = firstBlueprint.ToScreenSpace(delta) - firstBlueprint.ToScreenSpace(Vector2.Zero);
|
|
|
|
|
|
|
|
SelectionHandler.HandleMovement(new MoveSelectionEvent<ISkinnableDrawable>(firstBlueprint, delta));
|
|
|
|
}
|
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
protected override SelectionHandler<ISkinnableDrawable> CreateSelectionHandler() => new SkinSelectionHandler();
|
2021-04-28 14:14:48 +08:00
|
|
|
|
2021-05-13 16:06:00 +08:00
|
|
|
protected override SelectionBlueprint<ISkinnableDrawable> CreateBlueprintFor(ISkinnableDrawable component)
|
2021-04-28 14:14:48 +08:00
|
|
|
=> new SkinBlueprint(component);
|
2022-04-07 18:12:10 +08:00
|
|
|
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
|
|
{
|
|
|
|
base.Dispose(isDisposing);
|
|
|
|
|
|
|
|
foreach (var list in targetComponents)
|
|
|
|
list.UnbindAll();
|
|
|
|
}
|
2021-04-28 14:14:48 +08:00
|
|
|
}
|
|
|
|
}
|