Files
MovementTests/addons/forge/editor/statescript/resolvers/SignedAngleResolverEditor.cs
Minimata e09714cf83
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 27s
Create tag and build when new code gets to main / Export (push) Successful in 7m25s
update forge
2026-05-17 00:06:44 +02:00

398 lines
10 KiB
C#

// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
using Godot;
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
[Tool]
internal sealed partial class SignedAngleResolverEditor : NodeEditorProperty
{
private enum VectorSlot
{
From = 0,
To = 1,
}
private static readonly Type[] _vectorExpectedTypes = [typeof(SysVector2), typeof(SysVector3)];
private static readonly Type[] _axisExpectedTypes = [typeof(SysVector3)];
private StatescriptGraph? _graph;
private Action? _onChanged;
private VBoxContainer? _fromEditorContainer;
private VBoxContainer? _toEditorContainer;
private VBoxContainer? _axisEditorContainer;
private FoldableContainer? _fromFoldable;
private FoldableContainer? _toFoldable;
private FoldableContainer? _axisFoldable;
private NodeEditorProperty? _fromEditor;
private NodeEditorProperty? _toEditor;
private NodeEditorProperty? _axisEditor;
private CheckBox? _useAxisCheckBox;
private bool _useAxis;
private List<Func<NodeEditorProperty>> _vectorFactories = [];
private List<Func<NodeEditorProperty>> _axisFactories = [];
public override string DisplayName => "Signed Angle";
public override string ResolverTypeId => "SignedAngle";
public override bool IsCompatibleWith(Type expectedType)
{
return expectedType == typeof(float) || expectedType == typeof(ForgeVariant128);
}
public override void Setup(
StatescriptGraph graph,
StatescriptNodeProperty? property,
Type expectedType,
Action onChanged,
bool isArray)
{
_graph = graph;
_onChanged = onChanged;
_vectorFactories = ResolverEditorFactoryCatalog.GetCompatibleFactories(typeof(SysVector2), typeof(SysVector3));
_axisFactories = ResolverEditorFactoryCatalog.GetCompatibleFactories(typeof(SysVector3));
var existing = property?.Resolver as SignedAngleResolverResource;
_useAxis = existing?.Axis is not null;
SizeFlagsHorizontal = SizeFlags.ExpandFill;
var root = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(root);
BuildVectorSlot(
root,
VectorSlot.From,
"From:",
_vectorFactories,
existing?.From,
existing?.FromFolded ?? true,
x => _fromEditor = x);
BuildVectorSlot(
root,
VectorSlot.To,
"To:",
_vectorFactories,
existing?.To,
existing?.ToFolded ?? true,
x => _toEditor = x);
var axisToggleRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
root.AddChild(axisToggleRow);
_useAxisCheckBox = new CheckBox
{
Text = "Use Axis",
ButtonPressed = _useAxis,
SizeFlagsHorizontal = SizeFlags.ExpandFill,
};
_useAxisCheckBox.Toggled += OnUseAxisToggled;
axisToggleRow.AddChild(_useAxisCheckBox);
_axisFoldable = CreateFoldable("Axis:", existing?.AxisFolded ?? true);
_axisFoldable.Visible = _useAxis;
root.AddChild(_axisFoldable);
_axisEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_axisFoldable.AddChild(_axisEditorContainer);
BuildAxisEditor(existing?.Axis);
UpdateFoldableTitles();
}
public override void SaveTo(StatescriptNodeProperty property)
{
property.Resolver = new SignedAngleResolverResource
{
From = SaveNestedEditor(_fromEditor),
FromFolded = _fromFoldable?.Folded ?? false,
To = SaveNestedEditor(_toEditor),
ToFolded = _toFoldable?.Folded ?? false,
Axis = _useAxis ? SaveNestedEditor(_axisEditor) : null,
AxisFolded = _axisFoldable?.Folded ?? false,
};
}
public override void ClearCallbacks()
{
base.ClearCallbacks();
_onChanged = null;
_fromEditor?.ClearCallbacks();
_toEditor?.ClearCallbacks();
_axisEditor?.ClearCallbacks();
_fromEditorContainer = null;
_toEditorContainer = null;
_axisEditorContainer = null;
_fromFoldable = null;
_toFoldable = null;
_axisFoldable = null;
_fromEditor = null;
_toEditor = null;
_axisEditor = null;
_useAxisCheckBox = null;
}
private static StatescriptResolverResource? SaveNestedEditor(NodeEditorProperty? editor)
{
if (editor is null)
{
return null;
}
var property = new StatescriptNodeProperty();
editor.SaveTo(property);
return property.Resolver;
}
private static void ClearEditorContainer(VBoxContainer container)
{
foreach (Node child in container.GetChildren())
{
container.RemoveChild(child);
child.Free();
}
}
private static int GetSelectedIndex(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
return ResolverEditorFactoryCatalog.GetDefaultFactoryIndex(factories, existingResolver, "Variant");
}
private static OptionButton CreateResolverDropdown(
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver)
{
var dropdown = new OptionButton { SizeFlagsHorizontal = SizeFlags.ExpandFill };
foreach (Func<NodeEditorProperty> factory in factories)
{
dropdown.AddItem(StatescriptResolverRegistry.GetDisplayName(factory));
}
dropdown.Selected = GetSelectedIndex(factories, existingResolver);
return dropdown;
}
private void BuildVectorSlot(
VBoxContainer root,
VectorSlot slot,
string title,
List<Func<NodeEditorProperty>> factories,
StatescriptResolverResource? existingResolver,
bool folded,
Action<NodeEditorProperty?> setEditor)
{
FoldableContainer foldable = CreateFoldable(title, folded);
if (slot == VectorSlot.From)
{
_fromFoldable = foldable;
}
else
{
_toFoldable = foldable;
}
root.AddChild(foldable);
var container = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
foldable.AddChild(container);
OptionButton dropdown = CreateResolverDropdown(factories, existingResolver);
var editorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
if (slot == VectorSlot.From)
{
_fromEditorContainer = editorContainer;
}
else
{
_toEditorContainer = editorContainer;
}
container.AddChild(dropdown);
container.AddChild(editorContainer);
ShowEditor(
factories,
GetSelectedIndex(factories, existingResolver),
existingResolver,
_vectorExpectedTypes,
editorContainer,
setEditor);
if (slot == VectorSlot.From)
{
dropdown.ItemSelected += OnFromResolverDropdownItemSelected;
}
else
{
dropdown.ItemSelected += OnToResolverDropdownItemSelected;
}
}
private void BuildAxisEditor(StatescriptResolverResource? existingResolver)
{
if (_axisEditorContainer is null)
{
return;
}
ClearEditorContainer(_axisEditorContainer);
OptionButton dropdown = CreateResolverDropdown(_axisFactories, existingResolver);
_axisEditorContainer.AddChild(dropdown);
var nestedContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
_axisEditorContainer.AddChild(nestedContainer);
ShowEditor(
_axisFactories,
GetSelectedIndex(_axisFactories, existingResolver),
existingResolver,
_axisExpectedTypes,
nestedContainer,
x => _axisEditor = x);
dropdown.ItemSelected += OnAxisResolverDropdownItemSelected;
}
private void OnFromResolverDropdownItemSelected(long index)
{
HandleVectorResolverChanged(_fromEditorContainer, _vectorFactories, (int)index, x => _fromEditor = x);
}
private void OnToResolverDropdownItemSelected(long index)
{
HandleVectorResolverChanged(_toEditorContainer, _vectorFactories, (int)index, x => _toEditor = x);
}
private void OnAxisResolverDropdownItemSelected(long index)
{
VBoxContainer? nestedContainer = GetAxisNestedContainer();
if (nestedContainer is null)
{
return;
}
ClearEditorContainer(nestedContainer);
_axisEditor = null;
ShowEditor(_axisFactories, (int)index, null, _axisExpectedTypes, nestedContainer, x => _axisEditor = x);
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void HandleVectorResolverChanged(
VBoxContainer? editorContainer,
List<Func<NodeEditorProperty>> factories,
int index,
Action<NodeEditorProperty?> setEditor)
{
if (editorContainer is null)
{
return;
}
ClearEditorContainer(editorContainer);
setEditor(null);
ShowEditor(factories, index, null, _vectorExpectedTypes, editorContainer, setEditor);
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private VBoxContainer? GetAxisNestedContainer()
{
return _axisEditorContainer?.GetChildCount() > 1
? _axisEditorContainer.GetChild(1) as VBoxContainer
: null;
}
private FoldableContainer CreateFoldable(string title, bool folded)
{
var foldable = new FoldableContainer { Title = title, Folded = folded };
foldable.FoldingChanged += OnFoldingChanged;
return foldable;
}
private void OnFoldingChanged(bool isFolded)
{
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void OnUseAxisToggled(bool toggled)
{
_useAxis = toggled;
if (_axisEditorContainer?.GetParent() is FoldableContainer axisFoldable)
{
axisFoldable.Visible = toggled;
}
UpdateFoldableTitles();
_onChanged?.Invoke();
RaiseLayoutSizeChanged();
}
private void ShowEditor(
List<Func<NodeEditorProperty>> factories,
int factoryIndex,
StatescriptResolverResource? existingResolver,
Type[] allowedExpectedTypes,
VBoxContainer? container,
Action<NodeEditorProperty?> setEditor)
{
if (_graph is null || container is null || factoryIndex < 0 || factoryIndex >= factories.Count)
{
return;
}
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
_graph,
factories,
factoryIndex,
existingResolver,
allowedExpectedTypes,
OnNestedEditorChanged,
RaiseLayoutSizeChanged);
if (editor is null)
{
return;
}
container.AddChild(editor);
setEditor(editor);
}
private void OnNestedEditorChanged()
{
UpdateFoldableTitles();
_onChanged?.Invoke();
}
private void UpdateFoldableTitles()
{
if (_fromFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle("From:", _fromFoldable, _fromEditor);
}
if (_toFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle("To:", _toFoldable, _toEditor);
}
if (_axisFoldable is not null)
{
InlineConstantSummaryFormatter.ApplyFoldableTitle("Axis:", _axisFoldable, _axisEditor);
}
}
}
#endif