Compare commits
22 Commits
99f383be00
...
v0.2.86
| Author | SHA1 | Date | |
|---|---|---|---|
| e09714cf83 | |||
| 8b54f00814 | |||
| 415897b7b0 | |||
| d302d75238 | |||
| dc81796d52 | |||
| 2103832e46 | |||
| 1898d91a28 | |||
| b3ae3e37ea | |||
| 4cd67023d9 | |||
| afa335e7bf | |||
| a0e99a959f | |||
| 0cd942d90e | |||
| 150e007b22 | |||
| 01a2e7582b | |||
| 7ba4a3db3f | |||
| bcc748ca6b | |||
| 1db30eafd9 | |||
| 68e36742af | |||
| 33f55d04f3 | |||
| a139990390 | |||
| b2b7baffe8 | |||
| bed1384dc7 |
@@ -172,7 +172,13 @@ jobs:
|
||||
mkdir -v -p build/windows
|
||||
${{ steps.setup-godot.outputs.godot_bin }} --headless --verbose --export-release "Windows Desktop" build/windows/${{ env.GAME_NAME }}.exe
|
||||
ls -la build/windows
|
||||
|
||||
zip -r Windows.zip build/windows
|
||||
|
||||
- name: Upload build as artifact
|
||||
uses: actions/upload-artifact@v3-node20
|
||||
with:
|
||||
name: Windows build
|
||||
path: ${{ github.workspace }}/Windows.zip
|
||||
# - name: Setup Butler
|
||||
# shell: bash
|
||||
# env:
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
<Folder Include="tests\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Chickensoft.Sync" Version="2.3.0" />
|
||||
<PackageReference Include="RustyOptions" Version="0.10.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
BIN
addons/forge-godot-main.zip
Normal file
BIN
addons/forge-godot-main.zip
Normal file
Binary file not shown.
@@ -3,7 +3,6 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gamesmiths.Forge" Version="0.3.0" />
|
||||
<PackageReference Include="Gamesmiths.Forge" Version="0.3.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Gamesmiths.Forge.Core;
|
||||
using Gamesmiths.Forge.Godot.Core;
|
||||
using Gamesmiths.Forge.Godot.Editor;
|
||||
@@ -69,31 +68,35 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
Debug.Assert(
|
||||
_tagsEditorDock is not null,
|
||||
$"{nameof(_tagsEditorDock)} should have been initialized on _Ready().");
|
||||
Debug.Assert(
|
||||
_statescriptGraphEditorDock is not null,
|
||||
$"{nameof(_statescriptGraphEditorDock)} should have been initialized on _Ready().");
|
||||
|
||||
if (_fileSystem?.IsConnected(EditorFileSystem.SignalName.ResourcesReimported, _resourcesReimportedCallable)
|
||||
== true)
|
||||
{
|
||||
_fileSystem.Disconnect(EditorFileSystem.SignalName.ResourcesReimported, _resourcesReimportedCallable);
|
||||
}
|
||||
|
||||
RemoveDock(_tagsEditorDock);
|
||||
_tagsEditorDock.Free();
|
||||
if (_tagsEditorDock is not null)
|
||||
{
|
||||
RemoveDock(_tagsEditorDock);
|
||||
_tagsEditorDock.Free();
|
||||
_tagsEditorDock = null;
|
||||
}
|
||||
|
||||
RemoveInspectorPlugin(_tagContainerInspectorPlugin);
|
||||
RemoveInspectorPlugin(_tagInspectorPlugin);
|
||||
RemoveInspectorPlugin(_attributeSetInspectorPlugin);
|
||||
RemoveInspectorPlugin(_cueHandlerInspectorPlugin);
|
||||
RemoveInspectorPlugin(_attributeEditorPlugin);
|
||||
RemoveInspectorPlugin(_sharedVariableSetInspectorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _tagContainerInspectorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _tagInspectorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _attributeSetInspectorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _cueHandlerInspectorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _attributeEditorPlugin);
|
||||
RemoveInspectorPluginAndRelease(ref _sharedVariableSetInspectorPlugin);
|
||||
|
||||
RemoveDock(_statescriptGraphEditorDock);
|
||||
_statescriptGraphEditorDock.Free();
|
||||
if (_statescriptGraphEditorDock is not null)
|
||||
{
|
||||
RemoveDock(_statescriptGraphEditorDock);
|
||||
_statescriptGraphEditorDock.Free();
|
||||
_statescriptGraphEditorDock = null;
|
||||
}
|
||||
|
||||
_fileSystem = null;
|
||||
_resourcesReimportedCallable = default;
|
||||
|
||||
RemoveToolMenuItem("Repair assets tags");
|
||||
}
|
||||
@@ -132,7 +135,7 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
|
||||
EnsureForgeDataExists();
|
||||
|
||||
var config = ProjectSettings.LoadResourcePack(AutoloadPath);
|
||||
bool config = ProjectSettings.LoadResourcePack(AutoloadPath);
|
||||
|
||||
if (config)
|
||||
{
|
||||
@@ -173,7 +176,7 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
var paths = _statescriptGraphEditorDock.GetOpenResourcePaths();
|
||||
string[] paths = _statescriptGraphEditorDock.GetOpenResourcePaths();
|
||||
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
@@ -183,7 +186,7 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
configuration.SetValue("Forge", "open_tabs", string.Join(";", paths));
|
||||
configuration.SetValue("Forge", "active_tab", _statescriptGraphEditorDock.GetActiveTabIndex());
|
||||
|
||||
var varStates = _statescriptGraphEditorDock.GetVariablesPanelStates();
|
||||
bool[] varStates = _statescriptGraphEditorDock.GetVariablesPanelStates();
|
||||
configuration.SetValue("Forge", "variables_states", string.Join(";", varStates));
|
||||
}
|
||||
|
||||
@@ -197,26 +200,26 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
Variant tabsValue = configuration.GetValue("Forge", "open_tabs", string.Empty);
|
||||
Variant active = configuration.GetValue("Forge", "active_tab", -1);
|
||||
|
||||
var tabsString = tabsValue.AsString();
|
||||
string tabsString = tabsValue.AsString();
|
||||
if (string.IsNullOrEmpty(tabsString))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var paths = tabsString.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||
var activeIndex = active.AsInt32();
|
||||
string[] paths = tabsString.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||
int activeIndex = active.AsInt32();
|
||||
|
||||
bool[]? variablesStates = null;
|
||||
Variant varStatesValue = configuration.GetValue("Forge", "variables_states", string.Empty);
|
||||
var varString = varStatesValue.AsString();
|
||||
string varString = varStatesValue.AsString();
|
||||
|
||||
if (!string.IsNullOrEmpty(varString))
|
||||
{
|
||||
var parts = varString.Split(';');
|
||||
string[] parts = varString.Split(';');
|
||||
variablesStates = new bool[parts.Length];
|
||||
for (var i = 0; i < parts.Length; i++)
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
variablesStates[i] = bool.TryParse(parts[i], out var v) && v;
|
||||
variablesStates[i] = bool.TryParse(parts[i], out bool v) && v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,16 +251,28 @@ public partial class ForgePluginLoader : EditorPlugin
|
||||
AssetRepairTool.RepairAllAssetsTags();
|
||||
}
|
||||
|
||||
private void RemoveInspectorPluginAndRelease<TPlugin>(ref TPlugin? plugin)
|
||||
where TPlugin : EditorInspectorPlugin
|
||||
{
|
||||
if (plugin is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveInspectorPlugin(plugin);
|
||||
plugin = null;
|
||||
}
|
||||
|
||||
private void OnResourcesReimported(string[] resources)
|
||||
{
|
||||
foreach (var path in resources)
|
||||
foreach (string path in resources)
|
||||
{
|
||||
if (!ResourceLoader.Exists(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileType = EditorInterface.Singleton.GetResourceFilesystem().GetFileType(path);
|
||||
string fileType = EditorInterface.Singleton.GetResourceFilesystem().GetFileType(path);
|
||||
if (fileType != "StatescriptGraph" && fileType != "Resource")
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ForgeRandom : IRandom, IDisposable
|
||||
|
||||
public void NextBytes(byte[] buffer)
|
||||
{
|
||||
for (var i = 0; i < buffer.Length; i++)
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = (byte)_randomNumberGenerator.RandiRange(0, 255);
|
||||
}
|
||||
@@ -26,13 +26,25 @@ public class ForgeRandom : IRandom, IDisposable
|
||||
|
||||
public void NextBytes(Span<byte> buffer)
|
||||
{
|
||||
for (var i = 0; i < buffer.Length; i++)
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = (byte)_randomNumberGenerator.RandiRange(0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
public double NextDouble()
|
||||
{
|
||||
double value;
|
||||
do
|
||||
{
|
||||
value = _randomNumberGenerator.Randf();
|
||||
}
|
||||
while (value >= 1.0d);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public double NextDoubleInclusive()
|
||||
{
|
||||
return _randomNumberGenerator.Randf();
|
||||
}
|
||||
@@ -52,12 +64,17 @@ public class ForgeRandom : IRandom, IDisposable
|
||||
return _randomNumberGenerator.RandiRange(minValue, maxValue - 1);
|
||||
}
|
||||
|
||||
public int NextIntInclusive(int minValue, int maxValue)
|
||||
{
|
||||
return _randomNumberGenerator.RandiRange(minValue, maxValue);
|
||||
}
|
||||
|
||||
public long NextInt64()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var high = _randomNumberGenerator.Randi();
|
||||
var low = _randomNumberGenerator.Randi();
|
||||
uint high = _randomNumberGenerator.Randi();
|
||||
uint low = _randomNumberGenerator.Randi();
|
||||
return ((long)high << 32) | low;
|
||||
}
|
||||
}
|
||||
@@ -74,13 +91,47 @@ public class ForgeRandom : IRandom, IDisposable
|
||||
throw new ArgumentOutOfRangeException(nameof(minValue), "minValue must be less than maxValue.");
|
||||
}
|
||||
|
||||
var range = (ulong)(maxValue - minValue);
|
||||
var rand = (ulong)NextInt64();
|
||||
ulong range = (ulong)(maxValue - minValue);
|
||||
ulong rand = (ulong)NextInt64();
|
||||
|
||||
return (long)(rand % range) + minValue;
|
||||
}
|
||||
|
||||
public long NextInt64Inclusive(long minValue, long maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(minValue), "minValue must be less than or equal to maxValue.");
|
||||
}
|
||||
|
||||
if (minValue == maxValue)
|
||||
{
|
||||
return minValue;
|
||||
}
|
||||
|
||||
if (maxValue == long.MaxValue)
|
||||
{
|
||||
ulong inclusiveRange = (ulong)(maxValue - minValue) + 1UL;
|
||||
ulong rand = (ulong)NextInt64();
|
||||
return (long)(rand % inclusiveRange) + minValue;
|
||||
}
|
||||
|
||||
return NextInt64(minValue, maxValue + 1);
|
||||
}
|
||||
|
||||
public float NextSingle()
|
||||
{
|
||||
float value;
|
||||
do
|
||||
{
|
||||
value = _randomNumberGenerator.Randf();
|
||||
}
|
||||
while (value >= 1.0f);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public float NextSingleInclusive()
|
||||
{
|
||||
return _randomNumberGenerator.Randf();
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@ public static class StatescriptGraphBuilder
|
||||
continue;
|
||||
}
|
||||
|
||||
var outputPortIndex = connectionResource.OutputPort;
|
||||
var inputPortIndex = connectionResource.InputPort;
|
||||
int outputPortIndex = connectionResource.OutputPort;
|
||||
int inputPortIndex = connectionResource.InputPort;
|
||||
|
||||
if (outputPortIndex < 0 || outputPortIndex >= fromNode.OutputPorts.Length)
|
||||
{
|
||||
@@ -120,7 +120,7 @@ public static class StatescriptGraphBuilder
|
||||
if (variable.IsArray)
|
||||
{
|
||||
var initialValues = new Variant128[variable.InitialArrayValues.Count];
|
||||
for (var i = 0; i < variable.InitialArrayValues.Count; i++)
|
||||
for (int i = 0; i < variable.InitialArrayValues.Count; i++)
|
||||
{
|
||||
initialValues[i] = StatescriptVariableTypeConverter.GodotVariantToForge(
|
||||
variable.InitialArrayValues[i],
|
||||
@@ -167,7 +167,7 @@ public static class StatescriptGraphBuilder
|
||||
continue;
|
||||
}
|
||||
|
||||
var index = (byte)binding.PropertyIndex;
|
||||
byte index = (byte)binding.PropertyIndex;
|
||||
|
||||
if (binding.Direction == StatescriptPropertyDirection.Input)
|
||||
{
|
||||
@@ -251,11 +251,11 @@ public static class StatescriptGraphBuilder
|
||||
ConstructorInfo constructor = constructors.OrderByDescending(x => x.GetParameters().Length).First();
|
||||
ParameterInfo[] parameters = constructor.GetParameters();
|
||||
|
||||
var args = new object[parameters.Length];
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
object[] args = new object[parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
ParameterInfo param = parameters[i];
|
||||
var paramName = param.Name ?? string.Empty;
|
||||
string paramName = param.Name ?? string.Empty;
|
||||
|
||||
if (nodeResource.CustomData.TryGetValue(paramName, out GodotVariant value))
|
||||
{
|
||||
@@ -292,6 +292,20 @@ public static class StatescriptGraphBuilder
|
||||
|
||||
private static object ConvertParameter(GodotVariant value, Type targetType)
|
||||
{
|
||||
if (targetType.IsEnum)
|
||||
{
|
||||
if (value.VariantType == GodotVariant.Type.Int || value.VariantType == GodotVariant.Type.Float)
|
||||
{
|
||||
return Enum.ToObject(targetType, value.AsInt32());
|
||||
}
|
||||
|
||||
string enumText = value.AsString();
|
||||
if (!string.IsNullOrEmpty(enumText))
|
||||
{
|
||||
return Enum.Parse(targetType, enumText, ignoreCase: true);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetType == typeof(StringKey))
|
||||
{
|
||||
return new StringKey(value.AsString());
|
||||
|
||||
83
addons/forge/core/statescript/nodes/action/DebugNode.cs
Normal file
83
addons/forge/core/statescript/nodes/action/DebugNode.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Statescript.Nodes.Action;
|
||||
|
||||
/// <summary>
|
||||
/// Action node that resolves an input value of any supported type and prints it through
|
||||
/// <see cref="GD.Print(params Variant[])"/>.
|
||||
/// Useful for validating resolver chains while testing Statescript graphs in the editor.
|
||||
/// </summary>
|
||||
public sealed class DebugNode : ActionNode
|
||||
{
|
||||
private readonly StatescriptVariableType _valueType;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Description => "Prints the resolved input value to the Godot console for debugging.";
|
||||
|
||||
public DebugNode(StatescriptVariableType valueType = StatescriptVariableType.Int)
|
||||
{
|
||||
_valueType = valueType;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DefineParameters(
|
||||
List<InputProperty> inputProperties,
|
||||
List<OutputVariable> outputVariables)
|
||||
{
|
||||
inputProperties.Add(new InputProperty("Value", StatescriptVariableTypeConverter.ToSystemType(_valueType)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Execute(GraphContext graphContext)
|
||||
{
|
||||
if (!graphContext.TryResolveVariant(InputProperties[0].BoundName, out Variant128 value))
|
||||
{
|
||||
GD.Print("[Statescript Debug] <unresolved>");
|
||||
return;
|
||||
}
|
||||
|
||||
GD.Print("[Statescript Debug] ", FormatValue(value));
|
||||
}
|
||||
|
||||
private string FormatValue(Variant128 value)
|
||||
{
|
||||
return _valueType switch
|
||||
{
|
||||
StatescriptVariableType.Bool => value.AsBool().ToString(),
|
||||
StatescriptVariableType.Byte => value.AsByte().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.SByte => value.AsSByte().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Char => value.AsChar().ToString(),
|
||||
StatescriptVariableType.Decimal => value.AsDecimal().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Double => value.AsDouble().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Float => value.AsFloat().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Int => value.AsInt().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.UInt => value.AsUInt().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Long => value.AsLong().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.ULong => value.AsULong().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Short => value.AsShort().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.UShort => value.AsUShort().ToString(
|
||||
System.Globalization.CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Vector2 => value.AsVector2().ToString(),
|
||||
StatescriptVariableType.Vector3 => value.AsVector3().ToString(),
|
||||
StatescriptVariableType.Vector4 => value.AsVector4().ToString(),
|
||||
StatescriptVariableType.Plane => value.AsPlane().ToString(),
|
||||
StatescriptVariableType.Quaternion => value.AsQuaternion().ToString(),
|
||||
_ => Convert.ToHexString(value.ToBytes()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://byp7r8mspi3df
|
||||
@@ -23,12 +23,12 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
List<string> scenes = GetScenePaths("res://");
|
||||
GD.Print($"Found {scenes.Count} scene(s) to process.");
|
||||
|
||||
var openedScenes = EditorInterface.Singleton.GetOpenScenes();
|
||||
string[] openedScenes = EditorInterface.Singleton.GetOpenScenes();
|
||||
|
||||
foreach (var originalScenePath in scenes)
|
||||
foreach (string originalScenePath in scenes)
|
||||
{
|
||||
// For some weird reason scenes from the GetScenePath are coming with 3 slashes instead of just two.
|
||||
var scenePath = originalScenePath.Replace("res:///", "res://");
|
||||
string scenePath = originalScenePath.Replace("res:///", "res://");
|
||||
|
||||
GD.Print($"Processing scene: {scenePath}.");
|
||||
PackedScene? packedScene = ResourceLoader.Load<PackedScene>(scenePath);
|
||||
@@ -40,7 +40,7 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
}
|
||||
|
||||
Node sceneInstance = packedScene.Instantiate();
|
||||
var modified = ProcessNode(sceneInstance, tagsManager);
|
||||
bool modified = ProcessNode(sceneInstance, tagsManager);
|
||||
|
||||
if (!modified)
|
||||
{
|
||||
@@ -92,13 +92,13 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
dir.ListDirBegin();
|
||||
while (true)
|
||||
{
|
||||
var fileName = dir.GetNext();
|
||||
string fileName = dir.GetNext();
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var filePath = $"{basePath}/{fileName}";
|
||||
string filePath = $"{basePath}/{fileName}";
|
||||
if (dir.CurrentIsDir())
|
||||
{
|
||||
// Recursively scan subdirectories.
|
||||
@@ -123,7 +123,7 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
/// <returns><see langword="true"/> if any ForgeEntity was modified.</returns>
|
||||
private static bool ProcessNode(Node node, TagsManager tagsManager)
|
||||
{
|
||||
var modified = ValidateNode(node, tagsManager);
|
||||
bool modified = ValidateNode(node, tagsManager);
|
||||
|
||||
foreach (Node child in node.GetChildren())
|
||||
{
|
||||
@@ -135,7 +135,7 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
|
||||
private static bool ValidateNode(Node node, TagsManager tagsManager)
|
||||
{
|
||||
var modified = false;
|
||||
bool modified = false;
|
||||
foreach (Dictionary propertyInfo in node.GetPropertyList())
|
||||
{
|
||||
if (!propertyInfo.TryGetValue("class_name", out Variant className))
|
||||
@@ -153,7 +153,7 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyName = nameObj.AsString();
|
||||
string propertyName = nameObj.AsString();
|
||||
Variant value = node.Get(propertyName);
|
||||
|
||||
if (value.VariantType != Variant.Type.Object)
|
||||
@@ -182,9 +182,9 @@ public partial class AssetRepairTool : EditorPlugin
|
||||
|
||||
Array<string> originalTags = container.ContainerTags;
|
||||
var newTags = new Array<string>();
|
||||
var modified = false;
|
||||
bool modified = false;
|
||||
|
||||
foreach (var tag in originalTags)
|
||||
foreach (string tag in originalTags)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -11,7 +11,10 @@ public partial class AttributeEditorProperty : EditorProperty, ISerializationLis
|
||||
private const int ButtonSize = 26;
|
||||
private const int PopupSize = 300;
|
||||
|
||||
private Label _label = null!;
|
||||
private Label? _label;
|
||||
private Button? _button;
|
||||
private Popup? _popup;
|
||||
private Tree? _tree;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
@@ -19,20 +22,20 @@ public partial class AttributeEditorProperty : EditorProperty, ISerializationLis
|
||||
|
||||
var hBox = new HBoxContainer();
|
||||
_label = new Label { Text = "None", SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
var button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
|
||||
_button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
|
||||
|
||||
hBox.AddChild(_label);
|
||||
hBox.AddChild(button);
|
||||
hBox.AddChild(_button);
|
||||
AddChild(hBox);
|
||||
|
||||
var popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
|
||||
var tree = new Tree
|
||||
_popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
|
||||
_tree = new Tree
|
||||
{
|
||||
HideRoot = true,
|
||||
AnchorRight = 1,
|
||||
AnchorBottom = 1,
|
||||
};
|
||||
popup.AddChild(tree);
|
||||
_popup.AddChild(_tree);
|
||||
|
||||
var bg = new StyleBoxFlat
|
||||
{
|
||||
@@ -40,74 +43,125 @@ public partial class AttributeEditorProperty : EditorProperty, ISerializationLis
|
||||
.GetEditorTheme()
|
||||
.GetColor("dark_color_2", "Editor"),
|
||||
};
|
||||
tree.AddThemeStyleboxOverride("panel", bg);
|
||||
_tree.AddThemeStyleboxOverride("panel", bg);
|
||||
|
||||
AddChild(popup);
|
||||
AddChild(_popup);
|
||||
|
||||
BuildAttributeTree(tree);
|
||||
BuildAttributeTree(_tree);
|
||||
|
||||
button.Pressed += () =>
|
||||
{
|
||||
Window win = GetWindow();
|
||||
popup.Position = (Vector2I)button.GlobalPosition
|
||||
+ win.Position
|
||||
- new Vector2I(PopupSize - ButtonSize, -30);
|
||||
popup.Popup();
|
||||
};
|
||||
|
||||
tree.ItemActivated += () =>
|
||||
{
|
||||
TreeItem item = tree.GetSelected();
|
||||
if (item?.HasMeta("attribute_path") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var fullPath = item.GetMeta("attribute_path").AsString();
|
||||
_label.Text = fullPath;
|
||||
EmitChanged(GetEditedProperty(), fullPath);
|
||||
popup.Hide();
|
||||
};
|
||||
_button.Pressed += OnButtonPressed;
|
||||
_tree.ItemActivated += OnTreeItemActivated;
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
var value = GetEditedObject().Get(GetEditedProperty()).AsString();
|
||||
if (_label is null || !IsInstanceValid(_label))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string value = GetEditedObject().Get(GetEditedProperty()).AsString();
|
||||
_label.Text = string.IsNullOrEmpty(value) ? "None" : value;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
for (var i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
// This method was intentionally left blank.
|
||||
}
|
||||
|
||||
private static void BuildAttributeTree(Tree tree)
|
||||
{
|
||||
TreeItem root = tree.CreateItem();
|
||||
|
||||
foreach (var attributeSet in EditorUtils.GetAttributeSetOptions())
|
||||
foreach (string attributeSet in EditorUtils.GetAttributeSetOptions())
|
||||
{
|
||||
TreeItem setItem = tree.CreateItem(root);
|
||||
setItem.SetText(0, attributeSet);
|
||||
setItem.Collapsed = true;
|
||||
|
||||
foreach (var attribute in EditorUtils.GetAttributeOptions(attributeSet))
|
||||
foreach (string attribute in EditorUtils.GetAttributeOptions(attributeSet))
|
||||
{
|
||||
TreeItem attributeItem = tree.CreateItem(setItem);
|
||||
var attributePath = $"{attributeSet}.{attribute}";
|
||||
string attributePath = $"{attributeSet}.{attribute}";
|
||||
attributeItem.SetText(0, attribute);
|
||||
attributeItem.SetMeta("attribute_path", attributePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonPressed()
|
||||
{
|
||||
if (_button is null || _popup is null || !IsInstanceValid(_button) || !IsInstanceValid(_popup))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window win = GetWindow();
|
||||
_popup.Position = (Vector2I)_button.GlobalPosition
|
||||
+ win.Position
|
||||
- new Vector2I(PopupSize - ButtonSize, -30);
|
||||
_popup.Popup();
|
||||
}
|
||||
|
||||
private void OnTreeItemActivated()
|
||||
{
|
||||
if (_tree is null || _popup is null || _label is null
|
||||
|| !IsInstanceValid(_tree) || !IsInstanceValid(_popup) || !IsInstanceValid(_label))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem item = _tree.GetSelected();
|
||||
if (item?.HasMeta("attribute_path") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string fullPath = item.GetMeta("attribute_path").AsString();
|
||||
_label.Text = fullPath;
|
||||
EmitChanged(GetEditedProperty(), fullPath);
|
||||
_popup.Hide();
|
||||
}
|
||||
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_button is not null && IsInstanceValid(_button))
|
||||
{
|
||||
_button.Pressed -= OnButtonPressed;
|
||||
}
|
||||
|
||||
if (_tree is not null && IsInstanceValid(_tree))
|
||||
{
|
||||
_tree.ItemActivated -= OnTreeItemActivated;
|
||||
}
|
||||
|
||||
_label = null;
|
||||
_button = null;
|
||||
_popup = null;
|
||||
_tree = null;
|
||||
}
|
||||
|
||||
private void FreeAllChildren()
|
||||
{
|
||||
for (int i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Gamesmiths.Forge.Godot.Editor.Attributes;
|
||||
[Tool]
|
||||
public partial class AttributeSetClassEditorProperty : EditorProperty, ISerializationListener
|
||||
{
|
||||
private OptionButton _optionButton = null!;
|
||||
private OptionButton? _optionButton;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
@@ -22,60 +22,25 @@ public partial class AttributeSetClassEditorProperty : EditorProperty, ISerializ
|
||||
AddChild(_optionButton);
|
||||
|
||||
_optionButton.AddItem("Select AttributeSet Class");
|
||||
foreach (var option in EditorUtils.GetAttributeSetOptions())
|
||||
foreach (string option in EditorUtils.GetAttributeSetOptions())
|
||||
{
|
||||
_optionButton.AddItem(option);
|
||||
}
|
||||
|
||||
_optionButton.ItemSelected += x =>
|
||||
{
|
||||
var className = _optionButton.GetItemText((int)x);
|
||||
EmitChanged(GetEditedProperty(), className);
|
||||
|
||||
GodotObject @object = GetEditedObject();
|
||||
if (@object is not null)
|
||||
{
|
||||
var dictionary = new Dictionary<string, AttributeValues>();
|
||||
|
||||
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
|
||||
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
|
||||
if (targetType is not null)
|
||||
{
|
||||
System.Collections.Generic.IEnumerable<PropertyInfo> attributeProperties = targetType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.PropertyType == typeof(EntityAttribute));
|
||||
|
||||
foreach (var propertyName in attributeProperties.Select(x => x.Name))
|
||||
{
|
||||
if (@object is not ForgeAttributeSet forgeAttributeSet)
|
||||
{
|
||||
dictionary[propertyName] = new AttributeValues(0, 0, int.MaxValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributeSet? attributeSet = forgeAttributeSet.GetAttributeSet();
|
||||
if (attributeSet is null)
|
||||
{
|
||||
dictionary[propertyName] = new AttributeValues(0, 0, int.MaxValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityAttribute key = attributeSet.AttributesMap[className + "." + propertyName];
|
||||
dictionary[propertyName] = new AttributeValues(key.CurrentValue, key.Min, key.Max);
|
||||
}
|
||||
}
|
||||
|
||||
EmitChanged("InitialAttributeValues", dictionary);
|
||||
}
|
||||
};
|
||||
_optionButton.ItemSelected += OnItemSelected;
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
if (_optionButton is null || !IsInstanceValid(_optionButton))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GodotObject obj = GetEditedObject();
|
||||
StringName property = GetEditedProperty();
|
||||
var val = obj.Get(property).AsString();
|
||||
for (var i = 0; i < _optionButton.GetItemCount(); i++)
|
||||
string val = obj.Get(property).AsString();
|
||||
for (int i = 0; i < _optionButton.GetItemCount(); i++)
|
||||
{
|
||||
if (_optionButton.GetItemText(i) == val)
|
||||
{
|
||||
@@ -85,18 +50,90 @@ public partial class AttributeSetClassEditorProperty : EditorProperty, ISerializ
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
for (var i = GetChildCount() - 1; i >= 0; i--)
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnItemSelected(long index)
|
||||
{
|
||||
if (_optionButton is null || !IsInstanceValid(_optionButton))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string className = _optionButton.GetItemText((int)index);
|
||||
EmitChanged(GetEditedProperty(), className);
|
||||
|
||||
GodotObject @object = GetEditedObject();
|
||||
if (@object is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, AttributeValues>();
|
||||
|
||||
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
|
||||
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
|
||||
if (targetType is not null)
|
||||
{
|
||||
System.Collections.Generic.IEnumerable<PropertyInfo> attributeProperties = targetType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.PropertyType == typeof(EntityAttribute));
|
||||
|
||||
foreach (string? propertyName in attributeProperties.Select(x => x.Name))
|
||||
{
|
||||
if (@object is not ForgeAttributeSet forgeAttributeSet)
|
||||
{
|
||||
dictionary[propertyName] = new AttributeValues(0, 0, int.MaxValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributeSet? attributeSet = forgeAttributeSet.GetAttributeSet();
|
||||
if (attributeSet is null)
|
||||
{
|
||||
dictionary[propertyName] = new AttributeValues(0, 0, int.MaxValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityAttribute key = attributeSet.AttributesMap[className + "." + propertyName];
|
||||
dictionary[propertyName] = new AttributeValues(key.CurrentValue, key.Min, key.Max);
|
||||
}
|
||||
}
|
||||
|
||||
EmitChanged("InitialAttributeValues", dictionary);
|
||||
}
|
||||
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_optionButton is not null && IsInstanceValid(_optionButton))
|
||||
{
|
||||
_optionButton.ItemSelected -= OnItemSelected;
|
||||
}
|
||||
|
||||
_optionButton = null;
|
||||
}
|
||||
|
||||
private void FreeAllChildren()
|
||||
{
|
||||
for (int i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,7 @@ public partial class AttributeSetValuesEditorProperty : EditorProperty, ISeriali
|
||||
return;
|
||||
}
|
||||
|
||||
var className = obj.AttributeSetClass;
|
||||
string className = obj.AttributeSetClass;
|
||||
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
|
||||
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
|
||||
|
||||
@@ -53,7 +53,7 @@ public partial class AttributeSetValuesEditorProperty : EditorProperty, ISeriali
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(x => x.PropertyType == typeof(EntityAttribute));
|
||||
|
||||
foreach (var attributeName in attributeProperties.Select(x => x.Name))
|
||||
foreach (string? attributeName in attributeProperties.Select(x => x.Name))
|
||||
{
|
||||
var groupVBox = new VBoxContainer();
|
||||
|
||||
@@ -99,7 +99,7 @@ public partial class AttributeSetValuesEditorProperty : EditorProperty, ISeriali
|
||||
VBoxContainer? attributesRoot = GetNodeOrNull<VBoxContainer>("AttributesRoot");
|
||||
if (attributesRoot is not null)
|
||||
{
|
||||
for (var i = attributesRoot.GetChildCount() - 1; i >= 0; i--)
|
||||
for (int i = attributesRoot.GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = attributesRoot.GetChild(i);
|
||||
attributesRoot.RemoveChild(child);
|
||||
@@ -169,7 +169,7 @@ public partial class AttributeSetValuesEditorProperty : EditorProperty, ISeriali
|
||||
|
||||
private static void FreeAllChildren(Node node)
|
||||
{
|
||||
for (var i = node.GetChildCount() - 1; i >= 0; i--)
|
||||
for (int i = node.GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
node.GetChild(i).QueueFree();
|
||||
}
|
||||
|
||||
@@ -13,28 +13,22 @@ public partial class CueHandlerInspectorPlugin : EditorInspectorPlugin
|
||||
public override bool _CanHandle(GodotObject @object)
|
||||
{
|
||||
// Find out if its an implementation of CueHandler without having to add [Tool] attribute to them.
|
||||
try
|
||||
if (@object?.GetScript().As<CSharpScript>() is CSharpScript script)
|
||||
{
|
||||
if (@object?.GetScript().As<CSharpScript>() is null)
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
StringName className = script.GetGlobalName();
|
||||
|
||||
Type baseType = typeof(ForgeCueHandler);
|
||||
System.Reflection.Assembly assembly = baseType.Assembly;
|
||||
|
||||
Type? implementationType =
|
||||
Array.Find(assembly.GetTypes(), x =>
|
||||
x.Name == className &&
|
||||
baseType.IsAssignableFrom(x));
|
||||
|
||||
return implementationType is not null;
|
||||
}
|
||||
|
||||
var script = @object?.GetScript().As<CSharpScript>();
|
||||
StringName className = script.GetGlobalName();
|
||||
|
||||
Type baseType = typeof(ForgeCueHandler);
|
||||
System.Reflection.Assembly assembly = baseType.Assembly;
|
||||
|
||||
Type? implementationType =
|
||||
Array.Find(assembly.GetTypes(), x =>
|
||||
x.Name == className &&
|
||||
baseType.IsAssignableFrom(x));
|
||||
|
||||
return implementationType is not null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool _ParseProperty(
|
||||
|
||||
@@ -9,12 +9,15 @@ using Godot;
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Cues;
|
||||
|
||||
[Tool]
|
||||
public partial class CueKeyEditorProperty : EditorProperty
|
||||
public partial class CueKeyEditorProperty : EditorProperty, ISerializationListener
|
||||
{
|
||||
private const int ButtonSize = 26;
|
||||
private const int PopupSize = 300;
|
||||
|
||||
private Label _label = null!;
|
||||
private Label? _label;
|
||||
private Button? _button;
|
||||
private Popup? _popup;
|
||||
private Tree? _tree;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
@@ -24,72 +27,63 @@ public partial class CueKeyEditorProperty : EditorProperty
|
||||
|
||||
var hbox = new HBoxContainer();
|
||||
_label = new Label { Text = "None", SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
var button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
|
||||
_button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
|
||||
|
||||
hbox.AddChild(_label);
|
||||
hbox.AddChild(button);
|
||||
hbox.AddChild(_button);
|
||||
AddChild(hbox);
|
||||
|
||||
var popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
|
||||
var tree = new Tree
|
||||
_popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
|
||||
_tree = new Tree
|
||||
{
|
||||
HideRoot = true,
|
||||
AnchorRight = 1,
|
||||
AnchorBottom = 1,
|
||||
};
|
||||
popup.AddChild(tree);
|
||||
_popup.AddChild(_tree);
|
||||
|
||||
var backgroundStyle = new StyleBoxFlat
|
||||
{
|
||||
BgColor = EditorInterface.Singleton.GetEditorTheme().GetColor("base_color", "Editor"),
|
||||
};
|
||||
tree.AddThemeStyleboxOverride("panel", backgroundStyle);
|
||||
_tree.AddThemeStyleboxOverride("panel", backgroundStyle);
|
||||
|
||||
AddChild(popup);
|
||||
AddChild(_popup);
|
||||
|
||||
ForgeData pluginData = ResourceLoader.Load<ForgeData>(ForgeData.ForgeDataResourcePath);
|
||||
var tagsManager = new TagsManager([.. pluginData.RegisteredTags]);
|
||||
TreeItem root = tree.CreateItem();
|
||||
BuildTreeRecursively(tree, root, tagsManager.RootNode);
|
||||
TreeItem root = _tree.CreateItem();
|
||||
BuildTreeRecursively(_tree, root, tagsManager.RootNode);
|
||||
|
||||
button.Pressed += () =>
|
||||
{
|
||||
Window win = GetWindow();
|
||||
popup.Position = (Vector2I)button.GlobalPosition
|
||||
+ win.Position
|
||||
- new Vector2I(PopupSize - ButtonSize, -30);
|
||||
popup.Popup();
|
||||
};
|
||||
|
||||
tree.ItemActivated += () =>
|
||||
{
|
||||
TreeItem item = tree.GetSelected();
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build full path from root.
|
||||
var segments = new List<string>();
|
||||
TreeItem current = item;
|
||||
while (current.GetParent() is not null)
|
||||
{
|
||||
segments.Insert(0, current.GetText(0));
|
||||
current = current.GetParent();
|
||||
}
|
||||
|
||||
var fullPath = string.Join(".", segments);
|
||||
|
||||
_label.Text = fullPath;
|
||||
EmitChanged(GetEditedProperty(), fullPath);
|
||||
popup.Hide();
|
||||
};
|
||||
_button.Pressed += OnButtonPressed;
|
||||
_tree.ItemActivated += OnTreeItemActivated;
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
var property = GetEditedObject().Get(GetEditedProperty()).AsString();
|
||||
_label.Text = string.IsNullOrEmpty(property) ? "None" : property;
|
||||
string property = GetEditedObject().Get(GetEditedProperty()).AsString();
|
||||
|
||||
if (_label is not null && IsInstanceValid(_label))
|
||||
{
|
||||
_label.Text = string.IsNullOrEmpty(property) ? "None" : property;
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
}
|
||||
|
||||
private static void BuildTreeRecursively(Tree tree, TreeItem currentTreeItem, TagNode currentNode)
|
||||
@@ -102,5 +96,76 @@ public partial class CueKeyEditorProperty : EditorProperty
|
||||
BuildTreeRecursively(tree, childTreeNode, childTagNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonPressed()
|
||||
{
|
||||
if (_button is null || _popup is null || !IsInstanceValid(_button) || !IsInstanceValid(_popup))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window win = GetWindow();
|
||||
_popup.Position = (Vector2I)_button.GlobalPosition
|
||||
+ win.Position
|
||||
- new Vector2I(PopupSize - ButtonSize, -30);
|
||||
_popup.Popup();
|
||||
}
|
||||
|
||||
private void OnTreeItemActivated()
|
||||
{
|
||||
if (_tree is null || _popup is null || _label is null
|
||||
|| !IsInstanceValid(_tree) || !IsInstanceValid(_popup) || !IsInstanceValid(_label))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem item = _tree.GetSelected();
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var segments = new List<string>();
|
||||
TreeItem current = item;
|
||||
while (current.GetParent() is not null)
|
||||
{
|
||||
segments.Insert(0, current.GetText(0));
|
||||
current = current.GetParent();
|
||||
}
|
||||
|
||||
string fullPath = string.Join(".", segments);
|
||||
|
||||
_label.Text = fullPath;
|
||||
EmitChanged(GetEditedProperty(), fullPath);
|
||||
_popup.Hide();
|
||||
}
|
||||
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_button is not null && IsInstanceValid(_button))
|
||||
{
|
||||
_button.Pressed -= OnButtonPressed;
|
||||
}
|
||||
|
||||
if (_tree is not null && IsInstanceValid(_tree))
|
||||
{
|
||||
_tree.ItemActivated -= OnTreeItemActivated;
|
||||
}
|
||||
|
||||
_label = null;
|
||||
_button = null;
|
||||
_popup = null;
|
||||
_tree = null;
|
||||
}
|
||||
|
||||
private void FreeAllChildren()
|
||||
{
|
||||
for (int i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -172,6 +172,26 @@ internal abstract partial class CustomNodeEditor : RefCounted
|
||||
return _graphNode!.GetFoldStateInternal(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the persisted fold state for a given key, with a custom default when unset.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to persist the fold state.</param>
|
||||
/// <param name="defaultValue">The default fold state when no persisted value exists.</param>
|
||||
protected bool GetFoldState(string key, bool defaultValue)
|
||||
{
|
||||
return _graphNode!.GetFoldStateInternal(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persists a fold state change with undo support.
|
||||
/// </summary>
|
||||
/// <param name="key">The key used to persist the fold state.</param>
|
||||
/// <param name="folded">The new folded state.</param>
|
||||
protected void SetFoldStateWithUndo(string key, bool folded)
|
||||
{
|
||||
_graphNode!.SetFoldStateWithUndoInternal(key, folded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an existing property binding by direction and index.
|
||||
/// </summary>
|
||||
@@ -245,6 +265,14 @@ internal abstract partial class CustomNodeEditor : RefCounted
|
||||
_graphNode!.ResetSize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes standard input-property foldable summaries.
|
||||
/// </summary>
|
||||
protected void RefreshInputPropertyFoldableTitles()
|
||||
{
|
||||
_graphNode!.UpdateInputPropertyFoldableTitlesInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="StatescriptGraphNode.PropertyBindingChanged"/> event.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,656 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
internal static class InlineConstantSummaryFormatter
|
||||
{
|
||||
private const string SummaryBadgeMetaKey = "forge_inline_summary_badge";
|
||||
private const string SummaryBadgeInstanceIdMetaKey = "forge_inline_summary_badge_instance_id";
|
||||
private const string SummaryBadgeResizeHookMetaKey = "forge_inline_summary_badge_resize_hook";
|
||||
private const string SummaryBadgeKindMetaKey = "forge_inline_summary_badge_kind";
|
||||
private const string SummaryBadgeTextMetaKey = "forge_inline_summary_badge_text";
|
||||
private const string SummaryBadgeSelectedVariableMetaKey = "forge_inline_summary_badge_selected_variable";
|
||||
|
||||
private const string SummaryBadgeSelectedSharedVariableSetPathMetaKey =
|
||||
"forge_inline_summary_badge_selected_shared_set_path";
|
||||
|
||||
private const string SummaryBadgeSelectedSharedVariableMetaKey =
|
||||
"forge_inline_summary_badge_selected_shared_variable";
|
||||
|
||||
private const float MinimumBadgeWidth = 76f;
|
||||
private const float FoldableTitleChromeWidth = 30f;
|
||||
private const float FoldableTitleBadgeGap = 6f;
|
||||
private const float FoldableTitleRightPadding = 8f;
|
||||
|
||||
private static readonly Color _numericIconColor = new(0x3dbcc9ff);
|
||||
private static readonly Color _numericBackgroundColor = new(0x3dbcc918);
|
||||
private static readonly Color _numericBorderColor = new(0x3dbcc9ff);
|
||||
private static readonly Color _vectorIconColor = new(0xd48a3aff);
|
||||
private static readonly Color _vectorBackgroundColor = new(0xd48a3a18);
|
||||
private static readonly Color _vectorBorderColor = new(0xd48a3aff);
|
||||
private static readonly Color _booleanIconColor = new(0xc2a24fff);
|
||||
private static readonly Color _booleanBackgroundColor = new(0xc2a24f18);
|
||||
private static readonly Color _booleanBorderColor = new(0xc2a24fff);
|
||||
private static readonly Color _resolverIconColor = new(0xc06bcfff);
|
||||
private static readonly Color _resolverBackgroundColor = new(0xc06bcf18);
|
||||
private static readonly Color _resolverBorderColor = new(0xc06bcfff);
|
||||
private static readonly Color _variableIconColor = new(0x5d7be0ff);
|
||||
private static readonly Color _variableBackgroundColor = new(0x5d7be018);
|
||||
private static readonly Color _variableBorderColor = new(0x5d7be0ff);
|
||||
private static readonly Color _sharedVariableIconColor = new(0x46a86fff);
|
||||
private static readonly Color _sharedVariableBackgroundColor = new(0x46a86f18);
|
||||
private static readonly Color _sharedVariableBorderColor = new(0x46a86fff);
|
||||
private static readonly Color _enumIconColor = new(0xc0c6d1ff);
|
||||
private static readonly Color _enumBackgroundColor = new(0xc0c6d118);
|
||||
private static readonly Color _enumBorderColor = new(0xc0c6d1ff);
|
||||
|
||||
public static void ApplyFoldableTitle(
|
||||
string baseTitle,
|
||||
FoldableContainer foldable,
|
||||
NodeEditorProperty? editor)
|
||||
{
|
||||
EnsureResizeSyncHook(foldable);
|
||||
foldable.Title = baseTitle;
|
||||
|
||||
SummaryBadgeData badgeData = GetBadgeData(foldable, editor);
|
||||
PanelContainer badge = GetOrCreateSummaryBadge(foldable);
|
||||
ConfigureSummaryBadge(badge, badgeData);
|
||||
SynchronizeSiblingBadgeWidths(foldable);
|
||||
}
|
||||
|
||||
public static void ApplyFoldableTitle(
|
||||
string baseTitle,
|
||||
FoldableContainer foldable,
|
||||
string? summary,
|
||||
InlineSummaryBadgeKind badgeKind,
|
||||
bool isConstant = false,
|
||||
string? highlightedVariableName = null,
|
||||
string? highlightedSharedVariableSetPath = null,
|
||||
string? highlightedSharedVariableName = null)
|
||||
{
|
||||
EnsureResizeSyncHook(foldable);
|
||||
foldable.Title = baseTitle;
|
||||
|
||||
SummaryBadgeData badgeData = foldable.Folded && !string.IsNullOrWhiteSpace(summary)
|
||||
? CreateBadgeData(
|
||||
summary,
|
||||
badgeKind,
|
||||
isConstant,
|
||||
highlightedVariableName,
|
||||
highlightedSharedVariableSetPath,
|
||||
highlightedSharedVariableName)
|
||||
: SummaryBadgeData.Hidden;
|
||||
|
||||
PanelContainer badge = GetOrCreateSummaryBadge(foldable);
|
||||
ConfigureSummaryBadge(badge, badgeData);
|
||||
SynchronizeSiblingBadgeWidths(foldable);
|
||||
}
|
||||
|
||||
public static string GetFoldableTitle(
|
||||
string baseTitle,
|
||||
FoldableContainer foldable,
|
||||
NodeEditorProperty? editor)
|
||||
{
|
||||
if (!foldable.Folded || editor is null)
|
||||
{
|
||||
return baseTitle;
|
||||
}
|
||||
|
||||
if (editor.TryGetInlineSummary(out string summary) && !string.IsNullOrWhiteSpace(summary))
|
||||
{
|
||||
return $"{baseTitle} {summary}";
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(editor.DisplayName)
|
||||
? baseTitle
|
||||
: $"{baseTitle} {editor.DisplayName}";
|
||||
}
|
||||
|
||||
public static string FormatVariant(Variant value, StatescriptVariableType valueType)
|
||||
{
|
||||
return valueType switch
|
||||
{
|
||||
StatescriptVariableType.Bool => value.AsBool() ? "True" : "False",
|
||||
StatescriptVariableType.Byte => value.AsInt32().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.SByte => value.AsInt32().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Char => ((char)value.AsInt32()).ToString(),
|
||||
StatescriptVariableType.Decimal => value.AsDouble().ToString("G", CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Double => value.AsDouble().ToString("G", CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Float => value.AsSingle().ToString("G", CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Int => value.AsInt32().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.UInt => value.AsInt64().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Long => value.AsInt64().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.ULong => value.AsInt64().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Short => value.AsInt32().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.UShort => value.AsInt32().ToString(CultureInfo.InvariantCulture),
|
||||
StatescriptVariableType.Vector2 => FormatVector2(value.AsVector2()),
|
||||
StatescriptVariableType.Vector3 => FormatVector3(value.AsVector3()),
|
||||
StatescriptVariableType.Vector4 => FormatVector4(value.AsVector4()),
|
||||
StatescriptVariableType.Plane => FormatPlane(value.AsPlane()),
|
||||
StatescriptVariableType.Quaternion => FormatQuaternion(value.AsQuaternion()),
|
||||
_ => value.ToString(),
|
||||
};
|
||||
}
|
||||
|
||||
public static InlineSummaryBadgeKind GetBadgeKind(StatescriptVariableType valueType)
|
||||
{
|
||||
return valueType switch
|
||||
{
|
||||
StatescriptVariableType.Bool => InlineSummaryBadgeKind.Boolean,
|
||||
StatescriptVariableType.Vector2 => InlineSummaryBadgeKind.Vector,
|
||||
StatescriptVariableType.Vector3 => InlineSummaryBadgeKind.Vector,
|
||||
StatescriptVariableType.Vector4 => InlineSummaryBadgeKind.Vector,
|
||||
StatescriptVariableType.Plane => InlineSummaryBadgeKind.Vector,
|
||||
StatescriptVariableType.Quaternion => InlineSummaryBadgeKind.Vector,
|
||||
_ => InlineSummaryBadgeKind.Numeric,
|
||||
};
|
||||
}
|
||||
|
||||
internal static bool TryGetSummaryBadgeForHighlighting(
|
||||
FoldableContainer foldable,
|
||||
[NotNullWhen(true)] out PanelContainer? badge)
|
||||
{
|
||||
return TryGetSummaryBadge(foldable, out badge);
|
||||
}
|
||||
|
||||
private static SummaryBadgeData GetBadgeData(FoldableContainer foldable, NodeEditorProperty? editor)
|
||||
{
|
||||
if (!foldable.Folded || editor is null)
|
||||
{
|
||||
return SummaryBadgeData.Hidden;
|
||||
}
|
||||
|
||||
string? highlightedVariableName = null;
|
||||
string? highlightedSharedVariableSetPath = null;
|
||||
string? highlightedSharedVariableName = null;
|
||||
|
||||
if (editor.TryGetHighlightedVariableName(out string propagatedVariableName)
|
||||
&& !string.IsNullOrWhiteSpace(propagatedVariableName))
|
||||
{
|
||||
highlightedVariableName = propagatedVariableName;
|
||||
}
|
||||
|
||||
if (editor.TryGetHighlightedSharedVariable(
|
||||
out string propagatedSharedVariableSetPath,
|
||||
out string propagatedSharedVariableName)
|
||||
&& !string.IsNullOrWhiteSpace(propagatedSharedVariableSetPath)
|
||||
&& !string.IsNullOrWhiteSpace(propagatedSharedVariableName))
|
||||
{
|
||||
highlightedSharedVariableSetPath = propagatedSharedVariableSetPath;
|
||||
highlightedSharedVariableName = propagatedSharedVariableName;
|
||||
}
|
||||
|
||||
if (editor.TryGetInlineSummary(out string summary) && !string.IsNullOrWhiteSpace(summary))
|
||||
{
|
||||
InlineSummaryBadgeKind badgeKind = editor.GetInlineSummaryBadgeKind();
|
||||
return CreateBadgeData(
|
||||
summary,
|
||||
badgeKind,
|
||||
IsConstantBadgeKind(badgeKind),
|
||||
highlightedVariableName,
|
||||
highlightedSharedVariableSetPath,
|
||||
highlightedSharedVariableName);
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(editor.DisplayName)
|
||||
? SummaryBadgeData.Hidden
|
||||
: CreateBadgeData(
|
||||
editor.DisplayName,
|
||||
InlineSummaryBadgeKind.Resolver,
|
||||
false,
|
||||
highlightedVariableName,
|
||||
highlightedSharedVariableSetPath,
|
||||
highlightedSharedVariableName);
|
||||
}
|
||||
|
||||
private static SummaryBadgeData CreateBadgeData(
|
||||
string text,
|
||||
InlineSummaryBadgeKind badgeKind,
|
||||
bool isConstant,
|
||||
string? highlightedVariableName = null,
|
||||
string? highlightedSharedVariableSetPath = null,
|
||||
string? highlightedSharedVariableName = null)
|
||||
{
|
||||
BadgeVisualStyle style = GetBadgeStyle(badgeKind);
|
||||
return new SummaryBadgeData(
|
||||
GetBadgeIcon(badgeKind, isConstant),
|
||||
text,
|
||||
highlightedVariableName ?? string.Empty,
|
||||
highlightedSharedVariableSetPath ?? string.Empty,
|
||||
highlightedSharedVariableName ?? string.Empty,
|
||||
badgeKind,
|
||||
isConstant,
|
||||
style.IconColor,
|
||||
style.BackgroundColor,
|
||||
style.BorderColor,
|
||||
true);
|
||||
}
|
||||
|
||||
private static PanelContainer GetOrCreateSummaryBadge(FoldableContainer foldable)
|
||||
{
|
||||
if (TryGetSummaryBadge(foldable, out PanelContainer? existingBadge))
|
||||
{
|
||||
return existingBadge;
|
||||
}
|
||||
|
||||
var badge = new PanelContainer
|
||||
{
|
||||
Name = "InlineSummaryBadge",
|
||||
Visible = false,
|
||||
MouseFilter = Control.MouseFilterEnum.Ignore,
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter,
|
||||
};
|
||||
|
||||
var row = new HBoxContainer
|
||||
{
|
||||
Name = "Row",
|
||||
MouseFilter = Control.MouseFilterEnum.Ignore,
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
};
|
||||
row.AddThemeConstantOverride("separation", 4);
|
||||
badge.AddChild(row);
|
||||
|
||||
var iconLabel = new Label
|
||||
{
|
||||
Name = "Icon",
|
||||
MouseFilter = Control.MouseFilterEnum.Ignore,
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ShrinkBegin,
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
row.AddChild(iconLabel);
|
||||
|
||||
var textLabel = new Label
|
||||
{
|
||||
Name = "Text",
|
||||
MouseFilter = Control.MouseFilterEnum.Ignore,
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextOverrunBehavior = TextServer.OverrunBehavior.TrimEllipsis,
|
||||
};
|
||||
row.AddChild(textLabel);
|
||||
|
||||
foldable.AddTitleBarControl(badge);
|
||||
foldable.SetMeta(SummaryBadgeMetaKey, Variant.From(true));
|
||||
foldable.SetMeta(SummaryBadgeInstanceIdMetaKey, Variant.From((long)badge.GetInstanceId()));
|
||||
return badge;
|
||||
}
|
||||
|
||||
private static void EnsureResizeSyncHook(FoldableContainer foldable)
|
||||
{
|
||||
if (foldable.HasMeta(SummaryBadgeResizeHookMetaKey)
|
||||
&& foldable.GetMeta(SummaryBadgeResizeHookMetaKey).AsBool())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foldable.Resized += () => SynchronizeSiblingBadgeWidths(foldable);
|
||||
foldable.SetMeta(SummaryBadgeResizeHookMetaKey, Variant.From(true));
|
||||
}
|
||||
|
||||
private static void ConfigureSummaryBadge(PanelContainer badge, SummaryBadgeData badgeData)
|
||||
{
|
||||
badge.Visible = badgeData.Visible;
|
||||
if (!badgeData.Visible)
|
||||
{
|
||||
badge.SetMeta(SummaryBadgeKindMetaKey, Variant.From((int)InlineSummaryBadgeKind.Resolver));
|
||||
badge.SetMeta(SummaryBadgeTextMetaKey, Variant.From(string.Empty));
|
||||
badge.CustomMinimumSize = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
badge.SetMeta(SummaryBadgeKindMetaKey, Variant.From((int)badgeData.BadgeKind));
|
||||
badge.SetMeta(SummaryBadgeTextMetaKey, Variant.From(badgeData.Text));
|
||||
badge.SetMeta(
|
||||
"forge_inline_summary_badge_highlight_variable",
|
||||
Variant.From(badgeData.HighlightVariableName));
|
||||
badge.SetMeta(
|
||||
"forge_inline_summary_badge_highlight_shared_set_path",
|
||||
Variant.From(badgeData.HighlightSharedVariableSetPath));
|
||||
badge.SetMeta(
|
||||
"forge_inline_summary_badge_highlight_shared_variable",
|
||||
Variant.From(badgeData.HighlightSharedVariableName));
|
||||
|
||||
StyleBoxFlat styleBox = new()
|
||||
{
|
||||
BgColor = badgeData.BackgroundColor,
|
||||
BorderColor = badgeData.BorderColor,
|
||||
CornerRadiusTopLeft = 8,
|
||||
CornerRadiusTopRight = 8,
|
||||
CornerRadiusBottomRight = 8,
|
||||
CornerRadiusBottomLeft = 8,
|
||||
BorderWidthLeft = 1,
|
||||
BorderWidthTop = 1,
|
||||
BorderWidthRight = 1,
|
||||
BorderWidthBottom = 1,
|
||||
ContentMarginLeft = 8,
|
||||
ContentMarginTop = 3,
|
||||
ContentMarginRight = 8,
|
||||
ContentMarginBottom = 3,
|
||||
};
|
||||
|
||||
badge.AddThemeStyleboxOverride("panel", styleBox);
|
||||
|
||||
Label? iconLabel = badge.GetNodeOrNull<Label>("Row/Icon");
|
||||
Label? textLabel = badge.GetNodeOrNull<Label>("Row/Text");
|
||||
if (iconLabel is null || textLabel is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
iconLabel.SizeFlagsHorizontal = Control.SizeFlags.ShrinkBegin;
|
||||
textLabel.SizeFlagsHorizontal = Control.SizeFlags.ExpandFill;
|
||||
iconLabel.Text = badgeData.IconText;
|
||||
textLabel.Text = badgeData.Text;
|
||||
iconLabel.CustomMinimumSize = new Vector2(MeasureWidestBadgeIconWidth(iconLabel), 0);
|
||||
iconLabel.AddThemeColorOverride("font_color", badgeData.IconColor);
|
||||
textLabel.AddThemeColorOverride("font_color", textLabel.GetThemeColor("font_color", "Label"));
|
||||
textLabel.CustomMinimumSize = Vector2.Zero;
|
||||
|
||||
if (badge.HasMeta(SummaryBadgeSelectedVariableMetaKey))
|
||||
{
|
||||
string selectedVariableName = badge.GetMeta(SummaryBadgeSelectedVariableMetaKey).AsString();
|
||||
if (!string.IsNullOrEmpty(selectedVariableName)
|
||||
&& selectedVariableName == badgeData.HighlightVariableName)
|
||||
{
|
||||
styleBox.BorderWidthLeft = Math.Max(styleBox.BorderWidthLeft, 2);
|
||||
styleBox.BorderWidthTop = Math.Max(styleBox.BorderWidthTop, 2);
|
||||
styleBox.BorderWidthRight = Math.Max(styleBox.BorderWidthRight, 2);
|
||||
styleBox.BorderWidthBottom = Math.Max(styleBox.BorderWidthBottom, 2);
|
||||
styleBox.BorderColor = new Color(0x56b6c2ff);
|
||||
styleBox.BgColor = new Color(0x56b6c2ff);
|
||||
iconLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
textLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
}
|
||||
}
|
||||
|
||||
if (badge.HasMeta(SummaryBadgeSelectedSharedVariableSetPathMetaKey)
|
||||
&& badge.HasMeta(SummaryBadgeSelectedSharedVariableMetaKey))
|
||||
{
|
||||
string selectedSharedVariableSetPath = badge.GetMeta(SummaryBadgeSelectedSharedVariableSetPathMetaKey)
|
||||
.AsString();
|
||||
string selectedSharedVariableName = badge.GetMeta(SummaryBadgeSelectedSharedVariableMetaKey)
|
||||
.AsString();
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedSharedVariableSetPath)
|
||||
&& !string.IsNullOrEmpty(selectedSharedVariableName)
|
||||
&& selectedSharedVariableSetPath == badgeData.HighlightSharedVariableSetPath
|
||||
&& selectedSharedVariableName == badgeData.HighlightSharedVariableName)
|
||||
{
|
||||
styleBox.BorderWidthLeft = Math.Max(styleBox.BorderWidthLeft, 2);
|
||||
styleBox.BorderWidthTop = Math.Max(styleBox.BorderWidthTop, 2);
|
||||
styleBox.BorderWidthRight = Math.Max(styleBox.BorderWidthRight, 2);
|
||||
styleBox.BorderWidthBottom = Math.Max(styleBox.BorderWidthBottom, 2);
|
||||
styleBox.BorderColor = new Color(0x56b6c2ff);
|
||||
styleBox.BgColor = new Color(0x56b6c2ff);
|
||||
iconLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
textLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float MeasureWidestBadgeIconWidth(Label label)
|
||||
{
|
||||
float maxWidth = 0f;
|
||||
foreach (InlineSummaryBadgeKind badgeKind in Enum.GetValues<InlineSummaryBadgeKind>())
|
||||
{
|
||||
maxWidth = Math.Max(maxWidth, MeasureLabelTextWidth(label, GetBadgeIcon(badgeKind, false)));
|
||||
}
|
||||
|
||||
return Math.Max(maxWidth, MeasureLabelTextWidth(label, GetBadgeIcon(InlineSummaryBadgeKind.Resolver, true)));
|
||||
}
|
||||
|
||||
private static bool TryGetSummaryBadge(FoldableContainer foldable, [NotNullWhen(true)] out PanelContainer? badge)
|
||||
{
|
||||
badge = null;
|
||||
if (!foldable.HasMeta(SummaryBadgeMetaKey) || !foldable.HasMeta(SummaryBadgeInstanceIdMetaKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong badgeInstanceId = (ulong)foldable.GetMeta(SummaryBadgeInstanceIdMetaKey).AsInt64();
|
||||
if (badgeInstanceId == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GodotObject.InstanceFromId(badgeInstanceId) is PanelContainer existingBadge
|
||||
&& GodotObject.IsInstanceValid(existingBadge))
|
||||
{
|
||||
badge = existingBadge;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SynchronizeSiblingBadgeWidths(FoldableContainer foldable)
|
||||
{
|
||||
if (foldable.GetParent() is not Node parent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var siblingFoldables = new List<FoldableContainer>();
|
||||
var siblingBadges = new List<PanelContainer>();
|
||||
foreach (Node child in parent.GetChildren())
|
||||
{
|
||||
if (child is FoldableContainer siblingFoldable
|
||||
&& TryGetSummaryBadge(siblingFoldable, out PanelContainer? badge))
|
||||
{
|
||||
siblingFoldables.Add(siblingFoldable);
|
||||
siblingBadges.Add(badge);
|
||||
}
|
||||
}
|
||||
|
||||
if (siblingBadges.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (PanelContainer badge in siblingBadges)
|
||||
{
|
||||
badge.CustomMinimumSize = Vector2.Zero;
|
||||
}
|
||||
|
||||
float widestTitleWidth = 0;
|
||||
foreach (FoldableContainer siblingFoldable in siblingFoldables)
|
||||
{
|
||||
widestTitleWidth = Math.Max(widestTitleWidth, MeasureFoldableTitleWidth(siblingFoldable));
|
||||
}
|
||||
|
||||
float availableWidth = parent is Control parentControl
|
||||
? parentControl.Size.X
|
||||
- widestTitleWidth
|
||||
- FoldableTitleChromeWidth
|
||||
- FoldableTitleBadgeGap
|
||||
- FoldableTitleRightPadding
|
||||
: float.MaxValue;
|
||||
|
||||
float maxWidth = MinimumBadgeWidth;
|
||||
if (!float.IsPositiveInfinity(availableWidth) && !float.IsNaN(availableWidth))
|
||||
{
|
||||
maxWidth = Math.Max(0f, availableWidth);
|
||||
}
|
||||
|
||||
for (int i = 0; i < siblingBadges.Count; i++)
|
||||
{
|
||||
PanelContainer badge = siblingBadges[i];
|
||||
badge.CustomMinimumSize = badge.Visible
|
||||
? new Vector2(maxWidth, 0)
|
||||
: Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private static BadgeVisualStyle GetBadgeStyle(InlineSummaryBadgeKind badgeKind)
|
||||
{
|
||||
return badgeKind switch
|
||||
{
|
||||
InlineSummaryBadgeKind.Numeric => new BadgeVisualStyle(
|
||||
_numericIconColor,
|
||||
_numericBackgroundColor,
|
||||
_numericBorderColor),
|
||||
InlineSummaryBadgeKind.Vector => new BadgeVisualStyle(
|
||||
_vectorIconColor,
|
||||
_vectorBackgroundColor,
|
||||
_vectorBorderColor),
|
||||
InlineSummaryBadgeKind.Boolean => new BadgeVisualStyle(
|
||||
_booleanIconColor,
|
||||
_booleanBackgroundColor,
|
||||
_booleanBorderColor),
|
||||
InlineSummaryBadgeKind.Variable => new BadgeVisualStyle(
|
||||
_variableIconColor,
|
||||
_variableBackgroundColor,
|
||||
_variableBorderColor),
|
||||
InlineSummaryBadgeKind.SharedVariable => new BadgeVisualStyle(
|
||||
_sharedVariableIconColor,
|
||||
_sharedVariableBackgroundColor,
|
||||
_sharedVariableBorderColor),
|
||||
InlineSummaryBadgeKind.Enum => new BadgeVisualStyle(
|
||||
_enumIconColor,
|
||||
_enumBackgroundColor,
|
||||
_enumBorderColor),
|
||||
_ => new BadgeVisualStyle(
|
||||
_resolverIconColor,
|
||||
_resolverBackgroundColor,
|
||||
_resolverBorderColor),
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsConstantBadgeKind(InlineSummaryBadgeKind badgeKind)
|
||||
{
|
||||
return badgeKind is InlineSummaryBadgeKind.Numeric
|
||||
or InlineSummaryBadgeKind.Vector
|
||||
or InlineSummaryBadgeKind.Boolean;
|
||||
}
|
||||
|
||||
private static float MeasureFoldableTitleWidth(FoldableContainer foldable)
|
||||
{
|
||||
Font? font = foldable.GetThemeDefaultFont();
|
||||
if (font is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fontSize = foldable.GetThemeDefaultFontSize();
|
||||
return font.GetStringSize(
|
||||
foldable.Title,
|
||||
HorizontalAlignment.Left,
|
||||
-1,
|
||||
fontSize,
|
||||
TextServer.JustificationFlag.None,
|
||||
TextServer.Direction.Auto,
|
||||
TextServer.Orientation.Horizontal).X;
|
||||
}
|
||||
|
||||
private static string GetBadgeIcon(InlineSummaryBadgeKind badgeKind, bool isConstant)
|
||||
{
|
||||
return badgeKind switch
|
||||
{
|
||||
InlineSummaryBadgeKind.Numeric => "#",
|
||||
InlineSummaryBadgeKind.Vector => "▦",
|
||||
InlineSummaryBadgeKind.Boolean => "●",
|
||||
InlineSummaryBadgeKind.Variable => "𝑥",
|
||||
InlineSummaryBadgeKind.SharedVariable => "𝑦",
|
||||
InlineSummaryBadgeKind.Enum => "≡",
|
||||
_ => isConstant ? "#" : "ƒ",
|
||||
};
|
||||
}
|
||||
|
||||
private static float MeasureLabelTextWidth(Label label, string text)
|
||||
{
|
||||
Font? font = label.GetThemeFont("font", "Label") ?? label.GetThemeDefaultFont();
|
||||
if (font is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fontSize = label.GetThemeFontSize("font_size", "Label");
|
||||
if (fontSize <= 0)
|
||||
{
|
||||
fontSize = label.GetThemeDefaultFontSize();
|
||||
}
|
||||
|
||||
return font.GetStringSize(
|
||||
text,
|
||||
HorizontalAlignment.Left,
|
||||
-1,
|
||||
fontSize,
|
||||
TextServer.JustificationFlag.None,
|
||||
TextServer.Direction.Auto,
|
||||
TextServer.Orientation.Horizontal).X;
|
||||
}
|
||||
|
||||
private static string FormatVector2(Vector2 value)
|
||||
{
|
||||
return $"({FormatNumber(value.X)}, {FormatNumber(value.Y)})";
|
||||
}
|
||||
|
||||
private static string FormatVector3(Vector3 value)
|
||||
{
|
||||
return $"({FormatNumber(value.X)}, {FormatNumber(value.Y)}, {FormatNumber(value.Z)})";
|
||||
}
|
||||
|
||||
private static string FormatVector4(Vector4 value)
|
||||
{
|
||||
return $"({FormatNumber(value.X)}, {FormatNumber(value.Y)}, {FormatNumber(value.Z)}, {FormatNumber(value.W)})";
|
||||
}
|
||||
|
||||
private static string FormatPlane(Plane value)
|
||||
{
|
||||
return $"({FormatNumber(value.Normal.X)}, {FormatNumber(value.Normal.Y)}, {FormatNumber(value.Normal.Z)}, " +
|
||||
$"{FormatNumber(value.D)})";
|
||||
}
|
||||
|
||||
private static string FormatQuaternion(Quaternion value)
|
||||
{
|
||||
return $"({FormatNumber(value.X)}, {FormatNumber(value.Y)}, {FormatNumber(value.Z)}, {FormatNumber(value.W)})";
|
||||
}
|
||||
|
||||
private static string FormatNumber(float value)
|
||||
{
|
||||
return value.ToString("G", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private readonly record struct BadgeVisualStyle(
|
||||
Color IconColor,
|
||||
Color BackgroundColor,
|
||||
Color BorderColor);
|
||||
|
||||
private readonly record struct SummaryBadgeData(
|
||||
string IconText,
|
||||
string Text,
|
||||
string HighlightVariableName,
|
||||
string HighlightSharedVariableSetPath,
|
||||
string HighlightSharedVariableName,
|
||||
InlineSummaryBadgeKind BadgeKind,
|
||||
bool IsConstant,
|
||||
Color IconColor,
|
||||
Color BackgroundColor,
|
||||
Color BorderColor,
|
||||
bool Visible)
|
||||
{
|
||||
public static SummaryBadgeData Hidden => new(
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
InlineSummaryBadgeKind.Resolver,
|
||||
false,
|
||||
Colors.Transparent,
|
||||
Colors.Transparent,
|
||||
Colors.Transparent,
|
||||
false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://pn08sf5w3i4s
|
||||
16
addons/forge/editor/statescript/InlineSummaryBadgeKind.cs
Normal file
16
addons/forge/editor/statescript/InlineSummaryBadgeKind.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
internal enum InlineSummaryBadgeKind
|
||||
{
|
||||
Resolver = 0,
|
||||
Numeric = 1,
|
||||
Vector = 2,
|
||||
Boolean = 3,
|
||||
Variable = 4,
|
||||
SharedVariable = 5,
|
||||
Enum = 6,
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://b48mdbwtemd3y
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
internal sealed record InputPropertyFoldableContext(FoldableContainer Foldable, string BaseTitle);
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://cfavkpp486uto
|
||||
@@ -14,6 +14,8 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
[Tool]
|
||||
internal abstract partial class NodeEditorProperty : PanelContainer
|
||||
{
|
||||
private Type[] _allowedExpectedTypes = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name shown in the resolver type dropdown (e.g., "Variable", "Constant", "Attribute").
|
||||
/// </summary>
|
||||
@@ -58,6 +60,58 @@ internal abstract partial class NodeEditorProperty : PanelContainer
|
||||
/// </summary>
|
||||
public event Action? LayoutSizeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Configures the concrete input types allowed for this editor when the surrounding context accepts more than one.
|
||||
/// </summary>
|
||||
/// <param name="allowedExpectedTypes">The allowed expected types.</param>
|
||||
public void ConfigureAllowedExpectedTypes(params Type[] allowedExpectedTypes)
|
||||
{
|
||||
_allowedExpectedTypes = allowedExpectedTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to provide a short inline summary for the current editor state when embedded in a collapsed foldout.
|
||||
/// </summary>
|
||||
/// <param name="summary">The inline summary, when available.</param>
|
||||
/// <returns><see langword="true"/> when an inline summary is available.</returns>
|
||||
public virtual bool TryGetInlineSummary(out string summary)
|
||||
{
|
||||
summary = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the preferred badge style for inline foldout summaries.
|
||||
/// </summary>
|
||||
public virtual InlineSummaryBadgeKind GetInlineSummaryBadgeKind()
|
||||
{
|
||||
return InlineSummaryBadgeKind.Resolver;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to provide the graph-variable name represented by this editor or one of its nested editors for highlight
|
||||
/// propagation in folded summaries.
|
||||
/// </summary>
|
||||
/// <param name="variableName">The variable name, when available.</param>
|
||||
public virtual bool TryGetHighlightedVariableName(out string variableName)
|
||||
{
|
||||
variableName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to provide the shared-variable identity represented by this editor or one of its nested editors for
|
||||
/// highlight propagation in folded summaries.
|
||||
/// </summary>
|
||||
/// <param name="sharedVariableSetPath">The shared-variable set path, when available.</param>
|
||||
/// <param name="variableName">The shared variable name, when available.</param>
|
||||
public virtual bool TryGetHighlightedSharedVariable(out string sharedVariableSetPath, out string variableName)
|
||||
{
|
||||
sharedVariableSetPath = string.Empty;
|
||||
variableName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all delegate fields to prevent serialization issues during hot-reload. Called before the editor is
|
||||
/// serialized or freed.
|
||||
@@ -74,5 +128,10 @@ internal abstract partial class NodeEditorProperty : PanelContainer
|
||||
{
|
||||
LayoutSizeChanged?.Invoke();
|
||||
}
|
||||
|
||||
protected Type[] GetAllowedExpectedTypes(Type fallbackExpectedType)
|
||||
{
|
||||
return _allowedExpectedTypes.Length > 0 ? _allowedExpectedTypes : [fallbackExpectedType];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
internal static class SharedVariableHighlightState
|
||||
{
|
||||
private static string? _selectedSetPath;
|
||||
private static string? _selectedVariableName;
|
||||
private static string? _activeInspectorSetPath;
|
||||
|
||||
public static event Action? Changed;
|
||||
|
||||
public static void SetInspectorContext(string? setPath)
|
||||
{
|
||||
string normalized = string.IsNullOrWhiteSpace(setPath) ? string.Empty : setPath;
|
||||
if (string.Equals(_activeInspectorSetPath, normalized, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeInspectorSetPath = normalized;
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public static void ClearInspectorContext(string? setPath = null)
|
||||
{
|
||||
if (setPath is not null
|
||||
&& !string.Equals(_activeInspectorSetPath, setPath, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_activeInspectorSetPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeInspectorSetPath = string.Empty;
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public static void SetSelection(string? setPath, string? variableName)
|
||||
{
|
||||
string normalizedSetPath = string.IsNullOrWhiteSpace(setPath) ? string.Empty : setPath;
|
||||
string normalizedVariableName = string.IsNullOrWhiteSpace(variableName) ? string.Empty : variableName;
|
||||
|
||||
if (string.IsNullOrEmpty(normalizedSetPath) || string.IsNullOrEmpty(normalizedVariableName))
|
||||
{
|
||||
normalizedSetPath = string.Empty;
|
||||
normalizedVariableName = string.Empty;
|
||||
}
|
||||
|
||||
if (string.Equals(_selectedSetPath, normalizedSetPath, StringComparison.Ordinal)
|
||||
&& string.Equals(_selectedVariableName, normalizedVariableName, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedSetPath = normalizedSetPath;
|
||||
_selectedVariableName = normalizedVariableName;
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public static bool TryGetActiveSelection(out string setPath, out string variableName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_selectedSetPath)
|
||||
|| string.IsNullOrEmpty(_selectedVariableName)
|
||||
|| !string.Equals(_activeInspectorSetPath, _selectedSetPath, StringComparison.Ordinal))
|
||||
{
|
||||
setPath = string.Empty;
|
||||
variableName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
setPath = _selectedSetPath;
|
||||
variableName = _selectedVariableName;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool HasAnySelection()
|
||||
{
|
||||
return !string.IsNullOrEmpty(_selectedSetPath) && !string.IsNullOrEmpty(_selectedVariableName);
|
||||
}
|
||||
|
||||
public static bool IsSelected(string? setPath, string? variableName)
|
||||
{
|
||||
return TryGetActiveSelection(out string activeSetPath, out string activeVariableName)
|
||||
&& string.Equals(activeSetPath, setPath, StringComparison.Ordinal)
|
||||
&& string.Equals(activeVariableName, variableName, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://2gcypje3sxpa
|
||||
@@ -16,7 +16,15 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
[Tool]
|
||||
internal sealed partial class SharedVariableSetEditorProperty : EditorProperty, ISerializationListener
|
||||
{
|
||||
private const string BackgroundPanelNodeName = "BackgroundPanel";
|
||||
private const string RootNodeName = "Root";
|
||||
private const string HeaderRowNodeName = "HeaderRow";
|
||||
private const string AddButtonNodeName = "AddButton";
|
||||
private const string VariableListNodeName = "VariableList";
|
||||
private const string VariableNameButtonMetaKey = "_shared_variable_name_button";
|
||||
|
||||
private static readonly Color _variableColor = new(0xe5c07bff);
|
||||
private static readonly Color _highlightColor = new(0x56b6c2ff);
|
||||
|
||||
private readonly HashSet<string> _expandedArrays = [];
|
||||
|
||||
@@ -33,6 +41,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private Texture2D? _addIcon;
|
||||
private Texture2D? _removeIcon;
|
||||
private string? _selectedVariableName;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="EditorUndoRedoManager"/> used for undo/redo support.
|
||||
@@ -45,11 +54,21 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
SharedVariableHighlightState.Changed += OnSharedVariableHighlightChanged;
|
||||
|
||||
_addIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Add", "EditorIcons");
|
||||
_removeIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Remove", "EditorIcons");
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
SharedVariableHighlightState.SetSelection(sharedVariableSet.ResourcePath, _selectedVariableName);
|
||||
}
|
||||
|
||||
var backgroundPanel = new PanelContainer
|
||||
{
|
||||
Name = BackgroundPanelNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
|
||||
@@ -70,14 +89,19 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
AddChild(backgroundPanel);
|
||||
SetBottomEditor(backgroundPanel);
|
||||
|
||||
_root = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_root = new VBoxContainer
|
||||
{
|
||||
Name = RootNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
backgroundPanel.AddChild(_root);
|
||||
|
||||
var headerHBox = new HBoxContainer();
|
||||
var headerHBox = new HBoxContainer { Name = HeaderRowNodeName };
|
||||
_root.AddChild(headerHBox);
|
||||
|
||||
_addButton = new Button
|
||||
{
|
||||
Name = AddButtonNodeName,
|
||||
Text = "Add Variable",
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
@@ -87,34 +111,55 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
_root.AddChild(new HSeparator());
|
||||
|
||||
_variableList = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_variableList = new VBoxContainer
|
||||
{
|
||||
Name = VariableListNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
_root.AddChild(_variableList);
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
}
|
||||
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
RebuildList();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
SharedVariableHighlightState.Changed -= OnSharedVariableHighlightChanged;
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.ClearInspectorContext(sharedVariableSet.ResourcePath);
|
||||
}
|
||||
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
_creationDialog?.Free();
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
SharedVariableHighlightState.Changed -= OnSharedVariableHighlightChanged;
|
||||
ReleaseUiState();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
EnsureControlsCached();
|
||||
SharedVariableHighlightState.Changed += OnSharedVariableHighlightChanged;
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
_addButton.Pressed += OnAddPressed;
|
||||
}
|
||||
@@ -122,6 +167,28 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
RebuildList();
|
||||
}
|
||||
|
||||
private static int FindTypeDropdownIndex(OptionButton dropdown, StatescriptVariableType variableType)
|
||||
{
|
||||
for (int i = 0; i < dropdown.ItemCount; i++)
|
||||
{
|
||||
if (dropdown.GetItemId(i) == (int)variableType)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void UpdateVariableNameButtonAppearance(Button button, bool isSelected)
|
||||
{
|
||||
Color buttonColor = isSelected ? _highlightColor : _variableColor;
|
||||
button.AddThemeColorOverride("font_color", buttonColor);
|
||||
button.AddThemeColorOverride("font_pressed_color", buttonColor);
|
||||
button.AddThemeColorOverride("font_hover_color", buttonColor.Lightened(0.2f));
|
||||
button.AddThemeColorOverride("font_hover_pressed_color", buttonColor.Lightened(0.2f));
|
||||
}
|
||||
|
||||
private Array<ForgeSharedVariableDefinition> GetDefinitions()
|
||||
{
|
||||
GodotObject obj = GetEditedObject();
|
||||
@@ -133,7 +200,18 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void NotifyChanged()
|
||||
{
|
||||
if (GetEditedObject() is Resource resource)
|
||||
GodotObject obj = GetEditedObject();
|
||||
string propertyName = GetEditedProperty();
|
||||
|
||||
if (obj is not ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
obj.Set(propertyName, sharedVariableSet.Variables);
|
||||
EmitChanged(propertyName, sharedVariableSet.Variables);
|
||||
|
||||
if (obj is Resource resource)
|
||||
{
|
||||
resource.EmitChanged();
|
||||
}
|
||||
@@ -141,35 +219,31 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void RebuildList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer the actual rebuild so that any in-progress signal emission (e.g. a button Pressed handler that
|
||||
// triggered an add/remove) finishes before we free the emitting nodes.
|
||||
CallDeferred(MethodName.RebuildListDeferred);
|
||||
}
|
||||
|
||||
private void RebuildListDeferred()
|
||||
{
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
Array<ForgeSharedVariableDefinition> definitions = GetDefinitions();
|
||||
|
||||
for (var i = 0; i < definitions.Count; i++)
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
AddVariableRow(definitions, i);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void ClearVariableList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
@@ -182,6 +256,75 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureControlsCached()
|
||||
{
|
||||
_root ??= GetNodeOrNull<VBoxContainer>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}");
|
||||
_addButton ??= GetNodeOrNull<Button>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}/{HeaderRowNodeName}/{AddButtonNodeName}");
|
||||
_variableList ??= GetNodeOrNull<VBoxContainer>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}/{VariableListNodeName}");
|
||||
}
|
||||
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
if (_creationDialog is not null && IsInstanceValid(_creationDialog))
|
||||
{
|
||||
_creationDialog.Confirmed -= OnCreationConfirmed;
|
||||
_creationDialog.Canceled -= OnCreationCanceled;
|
||||
_creationDialog.Free();
|
||||
}
|
||||
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
_root = null;
|
||||
_variableList = null;
|
||||
_addButton = null;
|
||||
}
|
||||
|
||||
private void FreeAllChildren()
|
||||
{
|
||||
for (int i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSharedVariableHighlightChanged()
|
||||
{
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void SyncSelectedVariableFromHighlightState()
|
||||
{
|
||||
if (GetEditedObject() is not ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SharedVariableHighlightState.TryGetActiveSelection(out string selectedSetPath, out string variableName)
|
||||
&& string.Equals(selectedSetPath, sharedVariableSet.ResourcePath, System.StringComparison.Ordinal))
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
private void AddVariableRow(Array<ForgeSharedVariableDefinition> definitions, int index)
|
||||
{
|
||||
if (_variableList is null || index < 0 || index >= definitions.Count)
|
||||
@@ -197,17 +340,25 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
var headerRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
rowContainer.AddChild(headerRow);
|
||||
|
||||
var nameLabel = new Label
|
||||
bool isSelected = _selectedVariableName == def.VariableName;
|
||||
|
||||
var nameButton = new Button
|
||||
{
|
||||
Text = def.VariableName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
Flat = true,
|
||||
ToggleMode = true,
|
||||
ButtonPressed = isSelected,
|
||||
Alignment = HorizontalAlignment.Left,
|
||||
};
|
||||
|
||||
nameLabel.AddThemeColorOverride("font_color", _variableColor);
|
||||
nameLabel.AddThemeFontOverride(
|
||||
nameButton.SetMeta(VariableNameButtonMetaKey, def.VariableName);
|
||||
UpdateVariableNameButtonAppearance(nameButton, isSelected);
|
||||
nameButton.AddThemeFontOverride(
|
||||
"font",
|
||||
EditorInterface.Singleton.GetEditorTheme().GetFont("bold", "EditorFonts"));
|
||||
headerRow.AddChild(nameLabel);
|
||||
nameButton.Toggled += pressed => SetSelectedVariable(def.VariableName, pressed);
|
||||
headerRow.AddChild(nameButton);
|
||||
|
||||
var typeLabel = new Label
|
||||
{
|
||||
@@ -218,7 +369,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
typeLabel.AddThemeColorOverride("font_color", new Color(0.6f, 0.6f, 0.6f));
|
||||
headerRow.AddChild(typeLabel);
|
||||
|
||||
var capturedIndex = index;
|
||||
int capturedIndex = index;
|
||||
|
||||
var deleteButton = new Button
|
||||
{
|
||||
@@ -245,6 +396,56 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
rowContainer.AddChild(new HSeparator());
|
||||
}
|
||||
|
||||
private void SetSelectedVariable(string variableName, bool selected)
|
||||
{
|
||||
if (selected)
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
}
|
||||
else if (_selectedVariableName == variableName)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
SharedVariableHighlightState.SetSelection(sharedVariableSet.ResourcePath, _selectedVariableName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SharedVariableHighlightState.SetSelection(null, null);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisuals()
|
||||
{
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(_variableList);
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisualsRecursive(Node parent)
|
||||
{
|
||||
foreach (Node child in parent.GetChildren())
|
||||
{
|
||||
if (child is Button button && button.HasMeta(VariableNameButtonMetaKey))
|
||||
{
|
||||
string variableName = button.GetMeta(VariableNameButtonMetaKey).AsString();
|
||||
bool isSelected = _selectedVariableName == variableName;
|
||||
button.SetPressedNoSignal(isSelected);
|
||||
UpdateVariableNameButtonAppearance(button, isSelected);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
private Control CreateValueEditor(ForgeSharedVariableDefinition def)
|
||||
{
|
||||
if (def.VariableType == StatescriptVariableType.Bool)
|
||||
@@ -310,7 +511,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
var headerRow = new HBoxContainer();
|
||||
vBox.AddChild(headerRow);
|
||||
|
||||
var isExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
bool isExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
|
||||
var elementsContainer = new VBoxContainer
|
||||
{
|
||||
@@ -330,7 +531,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
{
|
||||
elementsContainer.Visible = x;
|
||||
|
||||
var wasExpanded = !x;
|
||||
bool wasExpanded = !x;
|
||||
|
||||
if (x)
|
||||
{
|
||||
@@ -379,9 +580,9 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
vBox.AddChild(elementsContainer);
|
||||
|
||||
for (var i = 0; i < def.InitialArrayValues.Count; i++)
|
||||
for (int i = 0; i < def.InitialArrayValues.Count; i++)
|
||||
{
|
||||
var capturedIndex = i;
|
||||
int capturedIndex = i;
|
||||
|
||||
if (def.VariableType == StatescriptVariableType.Bool)
|
||||
{
|
||||
@@ -502,7 +703,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void AddArrayElement(ForgeSharedVariableDefinition def, Variant value)
|
||||
{
|
||||
var wasExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
bool wasExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
@@ -583,6 +784,8 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
(int)variableType);
|
||||
}
|
||||
|
||||
_newTypeDropdown.Selected = FindTypeDropdownIndex(_newTypeDropdown, StatescriptVariableType.Int);
|
||||
|
||||
typeRow.AddChild(_newTypeDropdown);
|
||||
|
||||
var arrayRow = new HBoxContainer();
|
||||
@@ -605,7 +808,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
return;
|
||||
}
|
||||
|
||||
var name = _newNameEdit.Text.Trim();
|
||||
string name = _newNameEdit.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
|
||||
@@ -206,7 +206,7 @@ internal sealed partial class StatescriptAddNodeDialog : ConfirmationDialog, ISe
|
||||
IReadOnlyList<StatescriptNodeDiscovery.NodeTypeInfo> discoveredTypes =
|
||||
StatescriptNodeDiscovery.GetDiscoveredNodeTypes();
|
||||
|
||||
var filterLower = filter.ToLowerInvariant();
|
||||
string filterLower = filter.ToLowerInvariant();
|
||||
|
||||
TreeItem? actionCategory = null;
|
||||
TreeItem? conditionCategory = null;
|
||||
@@ -304,7 +304,7 @@ internal sealed partial class StatescriptAddNodeDialog : ConfirmationDialog, ISe
|
||||
|
||||
GetOkButton().Disabled = false;
|
||||
|
||||
var metadata = selected.GetMetadata(0).AsString();
|
||||
string metadata = selected.GetMetadata(0).AsString();
|
||||
UpdateDescription(metadata);
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ internal sealed partial class StatescriptAddNodeDialog : ConfirmationDialog, ISe
|
||||
return;
|
||||
}
|
||||
|
||||
var metadata = selected.GetMetadata(0).AsString();
|
||||
string metadata = selected.GetMetadata(0).AsString();
|
||||
|
||||
if (metadata == "__exit__")
|
||||
{
|
||||
|
||||
@@ -139,8 +139,8 @@ internal static partial class StatescriptEditorControls
|
||||
Func<int, double> getComponent,
|
||||
Action<double[]>? onChanged)
|
||||
{
|
||||
var componentCount = GetVectorComponentCount(type);
|
||||
var labels = GetVectorComponentLabels(type);
|
||||
int componentCount = GetVectorComponentCount(type);
|
||||
string[] labels = GetVectorComponentLabels(type);
|
||||
var vBox = new VBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
|
||||
var row = new HBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
@@ -153,11 +153,11 @@ internal static partial class StatescriptEditorControls
|
||||
vBox.AddChild(panelContainer);
|
||||
panelContainer.AddChild(row);
|
||||
|
||||
var values = new double[componentCount];
|
||||
double[] values = new double[componentCount];
|
||||
var handler = new VectorComponentHandler(values) { OnChanged = onChanged };
|
||||
vBox.AddChild(handler);
|
||||
|
||||
for (var i = 0; i < componentCount; i++)
|
||||
for (int i = 0; i < componentCount; i++)
|
||||
{
|
||||
values[i] = getComponent(i);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public partial class StatescriptGraphEditorDock
|
||||
return;
|
||||
}
|
||||
|
||||
var path = _newStatescriptPathEdit.Text.Trim();
|
||||
string path = _newStatescriptPathEdit.Text.Trim();
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
@@ -270,7 +270,7 @@ public partial class StatescriptGraphEditorDock
|
||||
{
|
||||
if (_pendingConnectionIsOutput)
|
||||
{
|
||||
var inputPort = FindFirstEnabledInputPort(newNodeId);
|
||||
int inputPort = FindFirstEnabledInputPort(newNodeId);
|
||||
if (inputPort >= 0)
|
||||
{
|
||||
OnConnectionRequest(
|
||||
@@ -282,7 +282,7 @@ public partial class StatescriptGraphEditorDock
|
||||
}
|
||||
else
|
||||
{
|
||||
var outputPort = FindFirstEnabledOutputPort(newNodeId);
|
||||
int outputPort = FindFirstEnabledOutputPort(newNodeId);
|
||||
if (outputPort >= 0)
|
||||
{
|
||||
OnConnectionRequest(
|
||||
|
||||
@@ -238,6 +238,7 @@ public partial class StatescriptGraphEditorDock
|
||||
|
||||
if (CurrentGraph == graph)
|
||||
{
|
||||
InvalidateCachedGraphVisuals(graph);
|
||||
LoadGraphIntoEditor(graph);
|
||||
}
|
||||
}
|
||||
@@ -339,7 +340,7 @@ public partial class StatescriptGraphEditorDock
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var nodeId = $"node_{_nextNodeId++}";
|
||||
string nodeId = $"node_{_nextNodeId++}";
|
||||
|
||||
var nodeResource = new StatescriptNode
|
||||
{
|
||||
@@ -371,10 +372,9 @@ public partial class StatescriptGraphEditorDock
|
||||
|
||||
if (CurrentGraph == graph && _graphEdit is not null)
|
||||
{
|
||||
var graphNode = new StatescriptGraphNode();
|
||||
_graphEdit.AddChild(graphNode);
|
||||
graphNode.Initialize(nodeResource, graph);
|
||||
graphNode.SetUndoRedo(_undoRedo);
|
||||
GraphTab? tab = FindTab(graph);
|
||||
StatescriptGraphNode graphNode = AddGraphNodeVisual(nodeResource, graph);
|
||||
tab?.CachedGraphNodes.Add(graphNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +384,7 @@ public partial class StatescriptGraphEditorDock
|
||||
|
||||
if (CurrentGraph == graph)
|
||||
{
|
||||
InvalidateCachedGraphVisuals(graph);
|
||||
LoadGraphIntoEditor(graph);
|
||||
}
|
||||
}
|
||||
@@ -424,7 +425,7 @@ public partial class StatescriptGraphEditorDock
|
||||
foreach (StatescriptGraphNode sgn in selectedNodes)
|
||||
{
|
||||
StatescriptNode original = sgn.NodeResource!;
|
||||
var newNodeId = $"node_{_nextNodeId++}";
|
||||
string newNodeId = $"node_{_nextNodeId++}";
|
||||
duplicatedIds[original.NodeId] = newNodeId;
|
||||
|
||||
var duplicated = new StatescriptNode
|
||||
@@ -457,16 +458,16 @@ public partial class StatescriptGraphEditorDock
|
||||
|
||||
graph.Nodes.Add(duplicated);
|
||||
|
||||
var graphNode = new StatescriptGraphNode();
|
||||
_graphEdit.AddChild(graphNode);
|
||||
graphNode.Initialize(duplicated, graph);
|
||||
GraphTab? tab = FindTab(graph);
|
||||
StatescriptGraphNode graphNode = AddGraphNodeVisual(duplicated, graph);
|
||||
tab?.CachedGraphNodes.Add(graphNode);
|
||||
graphNode.Selected = true;
|
||||
}
|
||||
|
||||
foreach (StatescriptConnection connection in graph.Connections)
|
||||
{
|
||||
if (duplicatedIds.TryGetValue(connection.FromNode, out var newFrom)
|
||||
&& duplicatedIds.TryGetValue(connection.ToNode, out var newTo))
|
||||
if (duplicatedIds.TryGetValue(connection.FromNode, out string? newFrom)
|
||||
&& duplicatedIds.TryGetValue(connection.ToNode, out string? newTo))
|
||||
{
|
||||
_graphEdit.ConnectNode(newFrom, connection.OutputPort, newTo, connection.InputPort);
|
||||
}
|
||||
|
||||
@@ -54,8 +54,11 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
private string[]? _serializedTabPaths;
|
||||
private int _serializedActiveTab = -1;
|
||||
private bool[]? _serializedVariablesStates;
|
||||
private string?[]? _serializedSelectedVariables;
|
||||
private string[]? _serializedConnections;
|
||||
private int[]? _serializedConnectionCounts;
|
||||
private bool _persistedVariablesPanelVisible = true;
|
||||
private bool _sharedVariableHighlightSubscribed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active graph resource, if any.
|
||||
@@ -92,13 +95,16 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
_filesystemChangedCallable = new Callable(this, nameof(OnFilesystemChanged));
|
||||
|
||||
_fileSystem.Connect(EditorFileSystem.SignalName.ResourcesReimported, _filesystemChangedCallable);
|
||||
SubscribeSharedVariableHighlightState();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
base._ExitTree();
|
||||
UnsubscribeSharedVariableHighlightState();
|
||||
|
||||
ClearGraphEditor();
|
||||
DisposeCachedGraphVisuals();
|
||||
_openTabs.Clear();
|
||||
|
||||
if (_fileSystem?.IsConnected(EditorFileSystem.SignalName.ResourcesReimported, _filesystemChangedCallable)
|
||||
@@ -108,10 +114,14 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
}
|
||||
|
||||
DisconnectUISignals();
|
||||
_fileSystem = null;
|
||||
_filesystemChangedCallable = default;
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
UnsubscribeSharedVariableHighlightState();
|
||||
|
||||
if (_fileSystem?.IsConnected(EditorFileSystem.SignalName.ResourcesReimported, _filesystemChangedCallable)
|
||||
== true)
|
||||
{
|
||||
@@ -121,6 +131,8 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
_serializedTabPaths = GetOpenResourcePaths();
|
||||
_serializedActiveTab = GetActiveTabIndex();
|
||||
_serializedVariablesStates = GetVariablesPanelStates();
|
||||
_serializedSelectedVariables = GetSelectedVariableStates();
|
||||
_persistedVariablesPanelVisible = _variablePanel?.Visible ?? _persistedVariablesPanelVisible;
|
||||
|
||||
SyncVisualNodePositionsToGraph();
|
||||
SyncConnectionsToCurrentGraph();
|
||||
@@ -133,10 +145,10 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
var allConnections = new List<string>();
|
||||
_serializedConnectionCounts = new int[_openTabs.Count];
|
||||
for (var i = 0; i < _openTabs.Count; i++)
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
StatescriptGraph graph = _openTabs[i].GraphResource;
|
||||
var count = 0;
|
||||
int count = 0;
|
||||
foreach (StatescriptConnection c in graph.Connections)
|
||||
{
|
||||
allConnections.Add($"{c.FromNode},{c.OutputPort},{c.ToNode},{c.InputPort}");
|
||||
@@ -159,6 +171,8 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
}
|
||||
}
|
||||
|
||||
DisposeCachedGraphVisuals();
|
||||
|
||||
_openTabs.Clear();
|
||||
}
|
||||
|
||||
@@ -173,6 +187,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
}
|
||||
|
||||
ConnectUISignals();
|
||||
SubscribeSharedVariableHighlightState();
|
||||
|
||||
if (_serializedTabPaths?.Length > 0)
|
||||
{
|
||||
@@ -210,23 +225,30 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _openTabs.Count; i++)
|
||||
PersistCurrentVariablePanelState();
|
||||
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
if (_openTabs[i].GraphResource == graph || (!string.IsNullOrEmpty(graph.ResourcePath)
|
||||
&& _openTabs[i].ResourcePath == graph.ResourcePath))
|
||||
{
|
||||
_tabBar.CurrentTab = i;
|
||||
SetCurrentTabWithoutLoading(i);
|
||||
ApplyVariablesPanelState(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
graph.EnsureEntryNode();
|
||||
|
||||
var tab = new GraphTab(graph);
|
||||
var tab = new GraphTab(graph)
|
||||
{
|
||||
VariablesPanelOpen = _variablePanel?.Visible ?? false,
|
||||
};
|
||||
|
||||
_openTabs.Add(tab);
|
||||
|
||||
_tabBar.AddTab(graph.StatescriptName);
|
||||
_tabBar.CurrentTab = _openTabs.Count - 1;
|
||||
SetCurrentTabWithoutLoading(_openTabs.Count - 1);
|
||||
|
||||
LoadGraphIntoEditor(graph);
|
||||
UpdateVisibility();
|
||||
@@ -242,7 +264,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTab = _tabBar.CurrentTab;
|
||||
int currentTab = _tabBar.CurrentTab;
|
||||
if (currentTab < 0 || currentTab >= _openTabs.Count)
|
||||
{
|
||||
return;
|
||||
@@ -257,7 +279,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
/// <returns>An array of resource paths.</returns>
|
||||
public string[] GetOpenResourcePaths()
|
||||
{
|
||||
return [.. _openTabs.Select(x => x.ResourcePath)];
|
||||
return [.. GetPersistedTabs().Select(x => x.ResourcePath)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -266,7 +288,35 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
/// <returns>The active tab index, or -1 if no tabs are open.</returns>
|
||||
public int GetActiveTabIndex()
|
||||
{
|
||||
return _tabBar?.CurrentTab ?? -1;
|
||||
PersistCurrentVariablePanelState();
|
||||
|
||||
if (_tabBar is null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int currentTab = _tabBar.CurrentTab;
|
||||
if (currentTab < 0 || currentTab >= _openTabs.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
GraphTab current = _openTabs[currentTab];
|
||||
if (string.IsNullOrEmpty(current.ResourcePath))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
GraphTab[] persistedTabs = GetPersistedTabs();
|
||||
for (int i = 0; i < persistedTabs.Length; i++)
|
||||
{
|
||||
if (persistedTabs[i].ResourcePath == current.ResourcePath)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -276,7 +326,14 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
/// </returns>
|
||||
public bool[] GetVariablesPanelStates()
|
||||
{
|
||||
return [.. _openTabs.Select(x => x.VariablesPanelOpen)];
|
||||
PersistCurrentVariablePanelState();
|
||||
return [.. GetPersistedTabs().Select(x => x.VariablesPanelOpen)];
|
||||
}
|
||||
|
||||
public string?[] GetSelectedVariableStates()
|
||||
{
|
||||
PersistCurrentVariablePanelState();
|
||||
return [.. GetPersistedTabs().Select(x => x.SelectedVariableName)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -331,10 +388,10 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
_tabBar.RemoveTab(0);
|
||||
}
|
||||
|
||||
var skippedTabs = 0;
|
||||
for (var i = 0; i < paths.Length; i++)
|
||||
int skippedTabs = 0;
|
||||
for (int i = 0; i < paths.Length; i++)
|
||||
{
|
||||
var path = paths[i];
|
||||
string path = paths[i];
|
||||
|
||||
StatescriptGraph? graph = LoadGraphFromPath(path);
|
||||
if (graph is null)
|
||||
@@ -346,7 +403,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
graph.EnsureEntryNode();
|
||||
var tab = new GraphTab(graph);
|
||||
|
||||
var currentTab = i - skippedTabs;
|
||||
int currentTab = i - skippedTabs;
|
||||
if (variablesStates is not null && currentTab < variablesStates.Length)
|
||||
{
|
||||
tab.VariablesPanelOpen = variablesStates[currentTab];
|
||||
@@ -360,10 +417,17 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
if (activeIndex >= 0 && activeIndex < _openTabs.Count)
|
||||
{
|
||||
_tabBar.CurrentTab = activeIndex;
|
||||
_openTabs[activeIndex].VariablesPanelOpen = _persistedVariablesPanelVisible;
|
||||
SetCurrentTabWithoutLoading(activeIndex);
|
||||
LoadGraphIntoEditor(_openTabs[activeIndex].GraphResource);
|
||||
ApplyVariablesPanelState(activeIndex);
|
||||
}
|
||||
else if (_openTabs.Count > 0)
|
||||
{
|
||||
SetCurrentTabWithoutLoading(0);
|
||||
LoadGraphIntoEditor(_openTabs[0].GraphResource);
|
||||
ApplyVariablesPanelState(0);
|
||||
}
|
||||
|
||||
UpdateVisibility();
|
||||
}
|
||||
@@ -388,7 +452,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
private static string GetBaseFilePath(string resourcePath)
|
||||
{
|
||||
var separatorIndex = resourcePath.IndexOf("::", StringComparison.Ordinal);
|
||||
int separatorIndex = resourcePath.IndexOf("::", StringComparison.Ordinal);
|
||||
return separatorIndex >= 0 ? resourcePath[..separatorIndex] : resourcePath;
|
||||
}
|
||||
|
||||
@@ -401,7 +465,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
{
|
||||
if (IsSubResourcePath(path))
|
||||
{
|
||||
var basePath = GetBaseFilePath(path);
|
||||
string basePath = GetBaseFilePath(path);
|
||||
if (!ResourceLoader.Exists(basePath))
|
||||
{
|
||||
return null;
|
||||
@@ -426,7 +490,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
private static StatescriptGraph? FindSubResourceGraph(Resource parentResource, string subResourcePath)
|
||||
{
|
||||
foreach (var propertyName in parentResource.GetPropertyList()
|
||||
foreach (string? propertyName in parentResource.GetPropertyList()
|
||||
.Select(p => p["name"].AsString()))
|
||||
{
|
||||
Variant value = parentResource.Get(propertyName);
|
||||
@@ -455,7 +519,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return graph;
|
||||
}
|
||||
|
||||
foreach (var propertyName in resource.GetPropertyList()
|
||||
foreach (string? propertyName in resource.GetPropertyList()
|
||||
.Select(p => p["name"].AsString()))
|
||||
{
|
||||
Variant value = resource.Get(propertyName);
|
||||
@@ -479,11 +543,11 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
private static void SaveGraphResource(StatescriptGraph graph)
|
||||
{
|
||||
var path = graph.ResourcePath;
|
||||
string path = graph.ResourcePath;
|
||||
|
||||
if (IsSubResourcePath(path))
|
||||
{
|
||||
var basePath = GetBaseFilePath(path);
|
||||
string basePath = GetBaseFilePath(path);
|
||||
Resource? parentResource = ResourceLoader.Load(basePath);
|
||||
if (parentResource is not null)
|
||||
{
|
||||
@@ -505,15 +569,17 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
var paths = _serializedTabPaths;
|
||||
var activeTab = _serializedActiveTab;
|
||||
var varStates = _serializedVariablesStates;
|
||||
var savedConnections = _serializedConnections;
|
||||
var connectionCounts = _serializedConnectionCounts;
|
||||
string[] paths = _serializedTabPaths;
|
||||
int activeTab = _serializedActiveTab;
|
||||
bool[]? varStates = _serializedVariablesStates;
|
||||
string?[]? selectedVariables = _serializedSelectedVariables;
|
||||
string[]? savedConnections = _serializedConnections;
|
||||
int[]? connectionCounts = _serializedConnectionCounts;
|
||||
|
||||
_serializedTabPaths = null;
|
||||
_serializedActiveTab = -1;
|
||||
_serializedVariablesStates = null;
|
||||
_serializedSelectedVariables = null;
|
||||
_serializedConnections = null;
|
||||
_serializedConnectionCounts = null;
|
||||
|
||||
@@ -530,8 +596,8 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
_tabBar.RemoveTab(0);
|
||||
}
|
||||
|
||||
var skippedTabs = 0;
|
||||
for (var i = 0; i < paths.Length; i++)
|
||||
int skippedTabs = 0;
|
||||
for (int i = 0; i < paths.Length; i++)
|
||||
{
|
||||
if (!ResourceLoader.Exists(paths[i]))
|
||||
{
|
||||
@@ -549,12 +615,17 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
graph.EnsureEntryNode();
|
||||
var tab = new GraphTab(graph);
|
||||
|
||||
var currentTab = i - skippedTabs;
|
||||
int currentTab = i - skippedTabs;
|
||||
if (varStates is not null && currentTab < varStates.Length)
|
||||
{
|
||||
tab.VariablesPanelOpen = varStates[currentTab];
|
||||
}
|
||||
|
||||
if (selectedVariables is not null && currentTab < selectedVariables.Length)
|
||||
{
|
||||
tab.SelectedVariableName = selectedVariables[currentTab];
|
||||
}
|
||||
|
||||
_openTabs.Add(tab);
|
||||
_tabBar.AddTab(graph.StatescriptName);
|
||||
}
|
||||
@@ -563,18 +634,18 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
if (savedConnections is not null && connectionCounts is not null)
|
||||
{
|
||||
var offset = 0;
|
||||
for (var i = 0; i < _openTabs.Count && i < connectionCounts.Length; i++)
|
||||
int offset = 0;
|
||||
for (int i = 0; i < _openTabs.Count && i < connectionCounts.Length; i++)
|
||||
{
|
||||
StatescriptGraph graph = _openTabs[i].GraphResource;
|
||||
graph.Connections.Clear();
|
||||
|
||||
for (var j = 0; j < connectionCounts[i] && offset < savedConnections.Length; j++, offset++)
|
||||
for (int j = 0; j < connectionCounts[i] && offset < savedConnections.Length; j++, offset++)
|
||||
{
|
||||
var parts = savedConnections[offset].Split(',');
|
||||
string[] parts = savedConnections[offset].Split(',');
|
||||
if (parts.Length != 4
|
||||
|| !int.TryParse(parts[1], out var outPort)
|
||||
|| !int.TryParse(parts[3], out var inPort))
|
||||
|| !int.TryParse(parts[1], out int outPort)
|
||||
|| !int.TryParse(parts[3], out int inPort))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -592,10 +663,17 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
if (activeTab >= 0 && activeTab < _openTabs.Count)
|
||||
{
|
||||
_tabBar.CurrentTab = activeTab;
|
||||
_openTabs[activeTab].VariablesPanelOpen = _persistedVariablesPanelVisible;
|
||||
SetCurrentTabWithoutLoading(activeTab);
|
||||
LoadGraphIntoEditor(_openTabs[activeTab].GraphResource);
|
||||
ApplyVariablesPanelState(activeTab);
|
||||
}
|
||||
else if (_openTabs.Count > 0)
|
||||
{
|
||||
SetCurrentTabWithoutLoading(0);
|
||||
LoadGraphIntoEditor(_openTabs[0].GraphResource);
|
||||
ApplyVariablesPanelState(0);
|
||||
}
|
||||
|
||||
UpdateVisibility();
|
||||
}
|
||||
@@ -607,13 +685,15 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
DisposeCachedGraphVisuals(_openTabs[tabIndex]);
|
||||
|
||||
_openTabs.RemoveAt(tabIndex);
|
||||
_tabBar.RemoveTab(tabIndex);
|
||||
|
||||
if (_openTabs.Count > 0)
|
||||
{
|
||||
var newTab = Mathf.Min(tabIndex, _openTabs.Count - 1);
|
||||
_tabBar.CurrentTab = newTab;
|
||||
int newTab = Mathf.Min(tabIndex, _openTabs.Count - 1);
|
||||
SetCurrentTabWithoutLoading(newTab);
|
||||
LoadGraphIntoEditor(_openTabs[newTab].GraphResource);
|
||||
ApplyVariablesPanelState(newTab);
|
||||
}
|
||||
@@ -820,7 +900,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
var hasOpenGraph = _openTabs.Count > 0;
|
||||
bool hasOpenGraph = _openTabs.Count > 0;
|
||||
|
||||
if (_splitContainer is not null)
|
||||
{
|
||||
@@ -855,22 +935,37 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
var wasLoading = _isLoadingGraph;
|
||||
GraphTab? tab = FindTab(graph);
|
||||
if (tab is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasLoading = _isLoadingGraph;
|
||||
_isLoadingGraph = true;
|
||||
|
||||
ClearGraphEditor();
|
||||
DetachVisibleGraphNodes();
|
||||
|
||||
_graphEdit.Zoom = graph.Zoom;
|
||||
|
||||
UpdateNextNodeId(graph);
|
||||
|
||||
foreach (StatescriptNode nodeResource in graph.Nodes)
|
||||
tab.CachedGraphNodes.RemoveAll(x => !IsInstanceValid(x));
|
||||
|
||||
if (tab.CachedGraphNodes.Count == 0)
|
||||
{
|
||||
var graphNode = new StatescriptGraphNode();
|
||||
_graphEdit.AddChild(graphNode);
|
||||
graphNode.Initialize(nodeResource, graph);
|
||||
graphNode.SetUndoRedo(_undoRedo);
|
||||
foreach (StatescriptNode nodeResource in graph.Nodes)
|
||||
{
|
||||
StatescriptGraphNode graphNode = AddGraphNodeVisual(nodeResource, graph);
|
||||
tab.CachedGraphNodes.Add(graphNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AttachCachedGraphNodes(tab);
|
||||
}
|
||||
|
||||
_graphEdit.ClearConnections();
|
||||
|
||||
foreach (StatescriptConnection connection in graph.Connections)
|
||||
{
|
||||
@@ -881,6 +976,8 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
connection.InputPort);
|
||||
}
|
||||
|
||||
ReapplyCurrentNodeHighlights();
|
||||
|
||||
_isLoadingGraph = wasLoading;
|
||||
|
||||
_ = ApplyScrollNextFrame(graph.ScrollOffset);
|
||||
@@ -912,21 +1009,137 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
{
|
||||
if (node is StatescriptGraphNode graphNode)
|
||||
{
|
||||
graphNode.OnBeforeSerialize();
|
||||
RemoveGraphNodeVisual(graphNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DetachVisibleGraphNodes()
|
||||
{
|
||||
if (_graphEdit is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_graphEdit.ClearConnections();
|
||||
|
||||
var toDetach = new List<Node>();
|
||||
toDetach.AddRange(_graphEdit.GetChildren().Where(x => x is GraphNode));
|
||||
|
||||
foreach (Node node in toDetach)
|
||||
{
|
||||
_graphEdit.RemoveChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
private StatescriptGraphNode AddGraphNodeVisual(StatescriptNode nodeResource, StatescriptGraph graph)
|
||||
{
|
||||
var graphNode = new StatescriptGraphNode();
|
||||
_graphEdit!.AddChild(graphNode);
|
||||
graphNode.Initialize(nodeResource, graph);
|
||||
graphNode.SetUndoRedo(_undoRedo);
|
||||
graphNode.PropertyBindingChanged += OnGraphNodePropertyBindingChanged;
|
||||
return graphNode;
|
||||
}
|
||||
|
||||
private void AttachCachedGraphNodes(GraphTab tab)
|
||||
{
|
||||
if (_graphEdit is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (StatescriptGraphNode graphNode in tab.CachedGraphNodes)
|
||||
{
|
||||
if (!IsInstanceValid(graphNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_graphEdit.RemoveChild(node);
|
||||
node.Free();
|
||||
if (graphNode.GetParent() is Node parent)
|
||||
{
|
||||
parent.RemoveChild(graphNode);
|
||||
}
|
||||
|
||||
_graphEdit.AddChild(graphNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveGraphNodeVisual(StatescriptGraphNode graphNode)
|
||||
{
|
||||
graphNode.PropertyBindingChanged -= OnGraphNodePropertyBindingChanged;
|
||||
graphNode.OnBeforeSerialize();
|
||||
|
||||
if (graphNode.GetParent() is Node parent)
|
||||
{
|
||||
parent.RemoveChild(graphNode);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
_openTabs[i].CachedGraphNodes.Remove(graphNode);
|
||||
}
|
||||
|
||||
graphNode.Free();
|
||||
}
|
||||
|
||||
private void DisposeCachedGraphVisuals()
|
||||
{
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
DisposeCachedGraphVisuals(_openTabs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeCachedGraphVisuals(GraphTab tab)
|
||||
{
|
||||
for (int i = tab.CachedGraphNodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
StatescriptGraphNode graphNode = tab.CachedGraphNodes[i];
|
||||
if (!IsInstanceValid(graphNode))
|
||||
{
|
||||
tab.CachedGraphNodes.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveGraphNodeVisual(graphNode);
|
||||
}
|
||||
}
|
||||
|
||||
private GraphTab? FindTab(StatescriptGraph graph)
|
||||
{
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
if (_openTabs[i].GraphResource == graph || (!string.IsNullOrEmpty(graph.ResourcePath)
|
||||
&& _openTabs[i].ResourcePath == graph.ResourcePath))
|
||||
{
|
||||
return _openTabs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetCurrentTabWithoutLoading(int tabIndex)
|
||||
{
|
||||
if (_tabBar is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasLoading = _isLoadingGraph;
|
||||
_isLoadingGraph = true;
|
||||
_tabBar.CurrentTab = tabIndex;
|
||||
_isLoadingGraph = wasLoading;
|
||||
}
|
||||
|
||||
private void UpdateNextNodeId(StatescriptGraph graph)
|
||||
{
|
||||
var maxId = 0;
|
||||
foreach (var nodeId in graph.Nodes.Select(x => x.NodeId))
|
||||
int maxId = 0;
|
||||
foreach (string? nodeId in graph.Nodes.Select(x => x.NodeId))
|
||||
{
|
||||
if (nodeId.StartsWith("node_", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& int.TryParse(nodeId["node_".Length..], out var id)
|
||||
&& int.TryParse(nodeId["node_".Length..], out int id)
|
||||
&& id >= maxId)
|
||||
{
|
||||
maxId = id + 1;
|
||||
@@ -946,7 +1159,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _openTabs.Count; i++)
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
_tabBar.SetTabTitle(i, _openTabs[i].GraphResource.StatescriptName);
|
||||
}
|
||||
@@ -990,7 +1203,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _openTabs.Count; i++)
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
if (i == newTabIndex)
|
||||
{
|
||||
@@ -1007,6 +1220,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
if (_variablePanel is not null)
|
||||
{
|
||||
_openTabs[i].VariablesPanelOpen = _variablePanel.Visible;
|
||||
_openTabs[i].SelectedVariableName = _variablePanel.GetSelectedVariableName();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1098,10 +1312,16 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
_variablePanel.Visible = pressed;
|
||||
|
||||
var current = _tabBar.CurrentTab;
|
||||
int current = _tabBar.CurrentTab;
|
||||
if (current >= 0 && current < _openTabs.Count)
|
||||
{
|
||||
_openTabs[current].VariablesPanelOpen = pressed;
|
||||
_persistedVariablesPanelVisible = pressed;
|
||||
|
||||
if (!pressed)
|
||||
{
|
||||
_openTabs[current].SelectedVariableName = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (pressed)
|
||||
@@ -1110,8 +1330,16 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
if (graph is not null)
|
||||
{
|
||||
_variablePanel.SetGraph(graph);
|
||||
if (current >= 0 && current < _openTabs.Count)
|
||||
{
|
||||
_variablePanel.RestoreSelectedVariable(_openTabs[current].SelectedVariableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_variablePanel.ClearSelectedVariable();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGraphVariablesChanged()
|
||||
@@ -1122,6 +1350,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateCachedGraphVisuals(graph);
|
||||
LoadGraphIntoEditor(graph);
|
||||
}
|
||||
|
||||
@@ -1131,29 +1360,125 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
}
|
||||
|
||||
private void OnVariableHighlightChanged(string? variableName)
|
||||
{
|
||||
int current = _tabBar?.CurrentTab ?? -1;
|
||||
if (current >= 0 && current < _openTabs.Count)
|
||||
{
|
||||
_openTabs[current].SelectedVariableName = variableName;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(variableName))
|
||||
{
|
||||
SharedVariableHighlightState.SetSelection(null, null);
|
||||
}
|
||||
|
||||
ReapplyCurrentNodeHighlights();
|
||||
}
|
||||
|
||||
private void OnGraphNodePropertyBindingChanged()
|
||||
{
|
||||
ReapplyCurrentNodeHighlights();
|
||||
}
|
||||
|
||||
private void SubscribeSharedVariableHighlightState()
|
||||
{
|
||||
if (_sharedVariableHighlightSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SharedVariableHighlightState.Changed += OnSharedVariableHighlightChanged;
|
||||
_sharedVariableHighlightSubscribed = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeSharedVariableHighlightState()
|
||||
{
|
||||
if (!_sharedVariableHighlightSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SharedVariableHighlightState.Changed -= OnSharedVariableHighlightChanged;
|
||||
_sharedVariableHighlightSubscribed = false;
|
||||
}
|
||||
|
||||
private void OnSharedVariableHighlightChanged()
|
||||
{
|
||||
if (SharedVariableHighlightState.HasAnySelection())
|
||||
{
|
||||
ClearGraphVariableSelections();
|
||||
return;
|
||||
}
|
||||
|
||||
ApplySharedVariableHighlightToNodes();
|
||||
}
|
||||
|
||||
private void ApplySharedVariableHighlightToNodes()
|
||||
{
|
||||
if (_graphEdit is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasSelection = SharedVariableHighlightState.TryGetActiveSelection(
|
||||
out string sharedVariableSetPath,
|
||||
out string sharedVariableName);
|
||||
|
||||
foreach (Node child in _graphEdit.GetChildren())
|
||||
{
|
||||
if (child is StatescriptGraphNode graphNode)
|
||||
{
|
||||
graphNode.SetHighlightedSharedVariable(
|
||||
hasSelection ? sharedVariableSetPath : null,
|
||||
hasSelection ? sharedVariableName : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReapplyCurrentNodeHighlights()
|
||||
{
|
||||
if (_graphEdit is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? selectedVariableName = null;
|
||||
int current = _tabBar?.CurrentTab ?? -1;
|
||||
if (current >= 0 && current < _openTabs.Count)
|
||||
{
|
||||
selectedVariableName = _openTabs[current].SelectedVariableName;
|
||||
}
|
||||
|
||||
foreach (Node child in _graphEdit.GetChildren())
|
||||
{
|
||||
if (child is StatescriptGraphNode graphNode)
|
||||
{
|
||||
graphNode.SetHighlightedVariable(variableName);
|
||||
graphNode.SetHighlightedVariable(selectedVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
ApplySharedVariableHighlightToNodes();
|
||||
}
|
||||
|
||||
private void ClearGraphVariableSelections()
|
||||
{
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
_openTabs[i].SelectedVariableName = null;
|
||||
}
|
||||
|
||||
if (_variablePanel is not null)
|
||||
{
|
||||
_variablePanel.ClearSelectedVariable();
|
||||
return;
|
||||
}
|
||||
|
||||
ReapplyCurrentNodeHighlights();
|
||||
}
|
||||
|
||||
private void EnsureVariablesPanelVisible()
|
||||
{
|
||||
if (_variablePanel is null || _variablesToggleButton is null || _openTabs.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_variablePanel.Visible)
|
||||
if (_variablePanel is null || _variablesToggleButton is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1161,7 +1486,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
_variablePanel.Visible = true;
|
||||
_variablesToggleButton.SetPressedNoSignal(true);
|
||||
|
||||
var current = _tabBar?.CurrentTab ?? -1;
|
||||
int current = _tabBar?.CurrentTab ?? -1;
|
||||
if (current >= 0 && current < _openTabs.Count)
|
||||
{
|
||||
_openTabs[current].VariablesPanelOpen = true;
|
||||
@@ -1174,23 +1499,44 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateCachedGraphVisuals(StatescriptGraph graph)
|
||||
{
|
||||
GraphTab? tab = FindTab(graph);
|
||||
if (tab is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = tab.CachedGraphNodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
StatescriptGraphNode graphNode = tab.CachedGraphNodes[i];
|
||||
if (!IsInstanceValid(graphNode))
|
||||
{
|
||||
tab.CachedGraphNodes.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoveGraphNodeVisual(graphNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilesystemChanged()
|
||||
{
|
||||
for (var i = 0; i < _openTabs.Count; i++)
|
||||
for (int i = 0; i < _openTabs.Count; i++)
|
||||
{
|
||||
_openTabs[i].UpdateCachedPathIfMissing();
|
||||
}
|
||||
|
||||
for (var i = _openTabs.Count - 1; i >= 0; i--)
|
||||
for (int i = _openTabs.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var path = _openTabs[i].ResourcePath;
|
||||
string path = _openTabs[i].ResourcePath;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var filePath = GetBaseFilePath(path);
|
||||
string filePath = GetBaseFilePath(path);
|
||||
if (!FileAccess.FileExists(filePath))
|
||||
{
|
||||
CloseTabByIndex(i);
|
||||
@@ -1208,14 +1554,50 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldShow = _openTabs[tabIndex].VariablesPanelOpen;
|
||||
bool shouldShow = _openTabs[tabIndex].VariablesPanelOpen;
|
||||
|
||||
if ((_tabBar?.CurrentTab ?? -1) == tabIndex)
|
||||
{
|
||||
shouldShow = _persistedVariablesPanelVisible;
|
||||
_openTabs[tabIndex].VariablesPanelOpen = shouldShow;
|
||||
}
|
||||
|
||||
_variablePanel.Visible = shouldShow;
|
||||
_variablesToggleButton.SetPressedNoSignal(shouldShow);
|
||||
|
||||
if (shouldShow)
|
||||
{
|
||||
_variablePanel.SetGraph(_openTabs[tabIndex].GraphResource);
|
||||
_variablePanel.RestoreSelectedVariable(_openTabs[tabIndex].SelectedVariableName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_variablePanel.ClearSelectedVariable();
|
||||
}
|
||||
}
|
||||
|
||||
private void PersistCurrentVariablePanelState()
|
||||
{
|
||||
if (_variablePanel is null || _tabBar is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int current = _tabBar.CurrentTab;
|
||||
if (current < 0 || current >= _openTabs.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_openTabs[current].VariablesPanelOpen = _variablePanel.Visible;
|
||||
_openTabs[current].SelectedVariableName = _variablePanel.GetSelectedVariableName();
|
||||
_persistedVariablesPanelVisible = _variablePanel.Visible;
|
||||
}
|
||||
|
||||
private GraphTab[] GetPersistedTabs()
|
||||
{
|
||||
PersistCurrentVariablePanelState();
|
||||
return [.. _openTabs.Where(x => !string.IsNullOrEmpty(x.ResourcePath))];
|
||||
}
|
||||
|
||||
private void OnGraphEditGuiInput(InputEvent @event)
|
||||
@@ -1350,7 +1732,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < graphNode.GetChildCount(); i++)
|
||||
for (int i = 0; i < graphNode.GetChildCount(); i++)
|
||||
{
|
||||
if (graphNode.IsSlotEnabledLeft(i))
|
||||
{
|
||||
@@ -1374,7 +1756,7 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < graphNode.GetChildCount(); i++)
|
||||
for (int i = 0; i < graphNode.GetChildCount(); i++)
|
||||
{
|
||||
if (graphNode.IsSlotEnabledRight(i))
|
||||
{
|
||||
@@ -1391,12 +1773,16 @@ public partial class StatescriptGraphEditorDock : EditorDock, ISerializationList
|
||||
|
||||
public StatescriptGraph GraphResource { get; }
|
||||
|
||||
public List<StatescriptGraphNode> CachedGraphNodes { get; } = [];
|
||||
|
||||
public string ResourcePath => !string.IsNullOrEmpty(GraphResource?.ResourcePath)
|
||||
? GraphResource.ResourcePath
|
||||
: _cachedPath;
|
||||
|
||||
public bool VariablesPanelOpen { get; set; }
|
||||
|
||||
public string? SelectedVariableName { get; set; }
|
||||
|
||||
public GraphTab(StatescriptGraph graphResource)
|
||||
{
|
||||
GraphResource = graphResource;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
@@ -9,17 +13,48 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
|
||||
public partial class StatescriptGraphNode
|
||||
{
|
||||
private bool ReferencesVariable(string variableName)
|
||||
private readonly Dictionary<OptionButton, StyleBoxFlat> _baseDropdownStyles = [];
|
||||
|
||||
private StyleBoxFlat? _basePanelStyle;
|
||||
private StyleBoxFlat? _baseSelectedPanelStyle;
|
||||
|
||||
private static bool ResolverReferencesVariable(StatescriptResolverResource? resolver, string variableName)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
if (resolver is null || string.IsNullOrEmpty(variableName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (StatescriptNodeProperty binding in NodeResource.PropertyBindings)
|
||||
var visited = new HashSet<nint>();
|
||||
return ResolverReferencesVariableRecursive(resolver, variableName, visited);
|
||||
}
|
||||
|
||||
private static bool ResolverReferencesVariableRecursive(
|
||||
StatescriptResolverResource resolver,
|
||||
string variableName,
|
||||
HashSet<nint> visited)
|
||||
{
|
||||
nint instanceId = (nint)resolver.GetInstanceId();
|
||||
if (!visited.Add(instanceId))
|
||||
{
|
||||
if (binding.Resolver is VariableResolverResource varRes
|
||||
&& varRes.VariableName == variableName)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolver is VariableResolverResource variableResolver
|
||||
&& string.Equals(variableResolver.VariableName, variableName, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (PropertyInfo property in resolver.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (!property.CanRead || !typeof(StatescriptResolverResource).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.GetValue(resolver) is StatescriptResolverResource nestedResolver
|
||||
&& ResolverReferencesVariableRecursive(nestedResolver, variableName, visited))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -28,23 +63,103 @@ public partial class StatescriptGraphNode
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ApplyHighlightBorder()
|
||||
private static bool ResolverReferencesSharedVariable(
|
||||
StatescriptResolverResource? resolver,
|
||||
string sharedVariableSetPath,
|
||||
string variableName)
|
||||
{
|
||||
if (_isHighlighted)
|
||||
if (resolver is null
|
||||
|| string.IsNullOrEmpty(sharedVariableSetPath)
|
||||
|| string.IsNullOrEmpty(variableName))
|
||||
{
|
||||
if (GetThemeStylebox("panel") is not StyleBoxFlat baseStyle)
|
||||
return false;
|
||||
}
|
||||
|
||||
var visited = new HashSet<nint>();
|
||||
return ResolverReferencesSharedVariableRecursive(resolver, sharedVariableSetPath, variableName, visited);
|
||||
}
|
||||
|
||||
private static bool ResolverReferencesSharedVariableRecursive(
|
||||
StatescriptResolverResource resolver,
|
||||
string sharedVariableSetPath,
|
||||
string variableName,
|
||||
HashSet<nint> visited)
|
||||
{
|
||||
nint instanceId = (nint)resolver.GetInstanceId();
|
||||
if (!visited.Add(instanceId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolver is SharedVariableResolverResource sharedVariableResolver
|
||||
&& string.Equals(sharedVariableResolver.SharedVariableSetPath, sharedVariableSetPath, StringComparison.Ordinal)
|
||||
&& string.Equals(sharedVariableResolver.VariableName, variableName, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (PropertyInfo property in resolver.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (!property.CanRead || !typeof(StatescriptResolverResource).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
var highlightStyle = (StyleBoxFlat)baseStyle.Duplicate();
|
||||
if (property.GetValue(resolver) is StatescriptResolverResource nestedResolver
|
||||
&& ResolverReferencesSharedVariableRecursive(
|
||||
nestedResolver,
|
||||
sharedVariableSetPath,
|
||||
variableName,
|
||||
visited))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ReferencesVariable(string variableName)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return NodeResource.PropertyBindings.Any(binding => ResolverReferencesVariable(binding.Resolver, variableName));
|
||||
}
|
||||
|
||||
private bool ReferencesSharedVariable(string? sharedVariableSetPath, string? variableName)
|
||||
{
|
||||
if (NodeResource is null
|
||||
|| string.IsNullOrEmpty(sharedVariableSetPath)
|
||||
|| string.IsNullOrEmpty(variableName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return NodeResource.PropertyBindings.Any(binding =>
|
||||
ResolverReferencesSharedVariable(binding.Resolver, sharedVariableSetPath, variableName));
|
||||
}
|
||||
|
||||
private void ApplyHighlightBorder()
|
||||
{
|
||||
EnsureBasePanelStylesStored();
|
||||
|
||||
if (_basePanelStyle is null || _baseSelectedPanelStyle is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isHighlighted)
|
||||
{
|
||||
StyleBoxFlat baseStyle = _basePanelStyle;
|
||||
var highlightStyle = (StyleBoxFlat)baseStyle.Duplicate();
|
||||
highlightStyle.BorderColor = _highlightColor;
|
||||
highlightStyle.BorderWidthTop = 2;
|
||||
highlightStyle.BorderWidthBottom = 2;
|
||||
highlightStyle.BorderWidthLeft = 2;
|
||||
highlightStyle.BorderWidthRight = 2;
|
||||
|
||||
highlightStyle.BgColor = baseStyle.BgColor.Lerp(_highlightColor, 0.15f);
|
||||
|
||||
AddThemeStyleboxOverride("panel", highlightStyle);
|
||||
@@ -52,10 +167,8 @@ public partial class StatescriptGraphNode
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveThemeStyleboxOverride("panel");
|
||||
RemoveThemeStyleboxOverride("panel_selected");
|
||||
|
||||
ApplyBottomPadding();
|
||||
AddThemeStyleboxOverride("panel", (StyleBoxFlat)_basePanelStyle.Duplicate());
|
||||
AddThemeStyleboxOverride("panel_selected", (StyleBoxFlat)_baseSelectedPanelStyle.Duplicate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,48 +189,107 @@ public partial class StatescriptGraphNode
|
||||
{
|
||||
HighlightLabelIfMatches(label);
|
||||
}
|
||||
else if (child is FoldableContainer foldable)
|
||||
{
|
||||
HighlightFoldableSummaryBadgeIfPresent(foldable);
|
||||
}
|
||||
else if (child is PanelContainer badge && badge.HasMeta("forge_inline_summary_badge_kind"))
|
||||
{
|
||||
HighlightSummaryBadgeIfMatches(badge);
|
||||
}
|
||||
|
||||
UpdateHighlightsRecursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void HighlightFoldableSummaryBadgeIfPresent(FoldableContainer foldable)
|
||||
{
|
||||
if (!InlineConstantSummaryFormatter.TryGetSummaryBadgeForHighlighting(foldable, out PanelContainer? badge))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HighlightSummaryBadgeIfMatches(badge);
|
||||
}
|
||||
|
||||
private void HighlightOptionButtonIfMatches(OptionButton dropdown)
|
||||
{
|
||||
if (!dropdown.HasMeta("is_variable_dropdown"))
|
||||
EnsureBaseDropdownStyleStored(dropdown);
|
||||
|
||||
if (!dropdown.HasMeta("is_variable_dropdown") && !dropdown.HasMeta("is_shared_variable_dropdown"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_highlightedVariableName))
|
||||
if (!_baseDropdownStyles.TryGetValue(dropdown, out StyleBoxFlat? baseStyle))
|
||||
{
|
||||
dropdown.RemoveThemeStyleboxOverride("normal");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedIdx = dropdown.Selected;
|
||||
if (string.IsNullOrEmpty(_highlightedVariableName)
|
||||
&& (string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
|
||||
|| string.IsNullOrEmpty(_highlightedSharedVariableName)))
|
||||
{
|
||||
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
|
||||
return;
|
||||
}
|
||||
|
||||
int selectedIdx = dropdown.Selected;
|
||||
if (selectedIdx < 0)
|
||||
{
|
||||
dropdown.RemoveThemeStyleboxOverride("normal");
|
||||
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedText = dropdown.GetItemText(selectedIdx);
|
||||
if (selectedText == _highlightedVariableName)
|
||||
string selectedText = dropdown.GetItemText(selectedIdx);
|
||||
bool isSharedVariableDropdown = dropdown.HasMeta("is_shared_variable_dropdown");
|
||||
string dropdownSetPath = dropdown.HasMeta("shared_variable_set_path")
|
||||
? dropdown.GetMeta("shared_variable_set_path").AsString()
|
||||
: string.Empty;
|
||||
|
||||
bool isMatch = isSharedVariableDropdown
|
||||
? !string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
|
||||
&& !string.IsNullOrEmpty(_highlightedSharedVariableName)
|
||||
&& selectedText == _highlightedSharedVariableName
|
||||
&& dropdownSetPath == _highlightedSharedVariableSetPath
|
||||
: selectedText == _highlightedVariableName;
|
||||
|
||||
if (isMatch)
|
||||
{
|
||||
if (dropdown.GetThemeStylebox("normal") is not StyleBoxFlat baseStyle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var highlightStyle = (StyleBoxFlat)baseStyle.Duplicate();
|
||||
|
||||
highlightStyle.BgColor = baseStyle.BgColor.Lerp(_highlightColor, 0.25f);
|
||||
|
||||
dropdown.AddThemeStyleboxOverride("normal", highlightStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
dropdown.RemoveThemeStyleboxOverride("normal");
|
||||
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureBasePanelStylesStored()
|
||||
{
|
||||
if (_basePanelStyle is null && GetThemeStylebox("panel") is StyleBoxFlat panelStyle)
|
||||
{
|
||||
_basePanelStyle = (StyleBoxFlat)panelStyle.Duplicate();
|
||||
}
|
||||
|
||||
if (_baseSelectedPanelStyle is null
|
||||
&& GetThemeStylebox("panel_selected") is StyleBoxFlat selectedPanelStyle)
|
||||
{
|
||||
_baseSelectedPanelStyle = (StyleBoxFlat)selectedPanelStyle.Duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureBaseDropdownStyleStored(OptionButton dropdown)
|
||||
{
|
||||
if (_baseDropdownStyles.ContainsKey(dropdown))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropdown.GetThemeStylebox("normal") is StyleBoxFlat baseStyle)
|
||||
{
|
||||
_baseDropdownStyles[dropdown] = (StyleBoxFlat)baseStyle.Duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,5 +317,75 @@ public partial class StatescriptGraphNode
|
||||
label.RemoveMeta("is_highlight_colored");
|
||||
}
|
||||
}
|
||||
|
||||
private void HighlightSummaryBadgeIfMatches(PanelContainer badge)
|
||||
{
|
||||
if (badge.GetNodeOrNull<Label>("Row/Text") is not Label textLabel
|
||||
|| badge.GetNodeOrNull<Label>("Row/Icon") is not Label iconLabel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!badge.HasMeta("forge_inline_summary_badge_base_stylebox")
|
||||
&& badge.GetThemeStylebox("panel") is StyleBoxFlat baseStyle)
|
||||
{
|
||||
badge.SetMeta("forge_inline_summary_badge_base_stylebox", Variant.From(baseStyle.Duplicate()));
|
||||
}
|
||||
|
||||
if (!badge.HasMeta("forge_inline_summary_badge_base_stylebox")
|
||||
|| badge.GetMeta("forge_inline_summary_badge_base_stylebox").Obj is not StyleBoxFlat storedBase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string propagatedVariableName = badge.HasMeta("forge_inline_summary_badge_highlight_variable")
|
||||
? badge.GetMeta("forge_inline_summary_badge_highlight_variable").AsString()
|
||||
: string.Empty;
|
||||
string propagatedSharedVariableName = badge.HasMeta("forge_inline_summary_badge_highlight_shared_variable")
|
||||
? badge.GetMeta("forge_inline_summary_badge_highlight_shared_variable").AsString()
|
||||
: string.Empty;
|
||||
string propagatedSharedVariableSetPath = badge.HasMeta("forge_inline_summary_badge_highlight_shared_set_path")
|
||||
? badge.GetMeta("forge_inline_summary_badge_highlight_shared_set_path").AsString()
|
||||
: string.Empty;
|
||||
|
||||
bool isMatch = !string.IsNullOrEmpty(_highlightedVariableName)
|
||||
&& (textLabel.Text == _highlightedVariableName
|
||||
|| propagatedVariableName == _highlightedVariableName);
|
||||
|
||||
isMatch = isMatch
|
||||
|| (!string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
|
||||
&& !string.IsNullOrEmpty(_highlightedSharedVariableName)
|
||||
&& (textLabel.Text == _highlightedSharedVariableName
|
||||
|| propagatedSharedVariableName == _highlightedSharedVariableName)
|
||||
&& propagatedSharedVariableSetPath == _highlightedSharedVariableSetPath);
|
||||
|
||||
badge.SetMeta("forge_inline_summary_badge_selected_variable", Variant.From(_highlightedVariableName ?? string.Empty));
|
||||
badge.SetMeta(
|
||||
"forge_inline_summary_badge_selected_shared_set_path",
|
||||
Variant.From(_highlightedSharedVariableSetPath ?? string.Empty));
|
||||
badge.SetMeta(
|
||||
"forge_inline_summary_badge_selected_shared_variable",
|
||||
Variant.From(_highlightedSharedVariableName ?? string.Empty));
|
||||
|
||||
var style = (StyleBoxFlat)storedBase.Duplicate();
|
||||
if (isMatch)
|
||||
{
|
||||
style.BorderWidthLeft = Math.Max(style.BorderWidthLeft, 2);
|
||||
style.BorderWidthTop = Math.Max(style.BorderWidthTop, 2);
|
||||
style.BorderWidthRight = Math.Max(style.BorderWidthRight, 2);
|
||||
style.BorderWidthBottom = Math.Max(style.BorderWidthBottom, 2);
|
||||
style.BorderColor = _highlightColor;
|
||||
style.BgColor = _highlightColor;
|
||||
iconLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
textLabel.AddThemeColorOverride("font_color", Colors.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
iconLabel.RemoveThemeColorOverride("font_color");
|
||||
textLabel.RemoveThemeColorOverride("font_color");
|
||||
}
|
||||
|
||||
badge.AddThemeStyleboxOverride("panel", style);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,23 +24,43 @@ public partial class StatescriptGraphNode
|
||||
}
|
||||
|
||||
var container = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
sectionContainer.AddChild(container);
|
||||
var key = new PropertySlotKey(StatescriptPropertyDirection.Input, index);
|
||||
string baseTitle = $"{propInfo.Label}:";
|
||||
|
||||
var propertyFoldable = new FoldableContainer
|
||||
{
|
||||
Title = baseTitle,
|
||||
Folded = GetFoldState(GetInputPropertyFoldKey(index), true),
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
|
||||
_foldableKeys[propertyFoldable] = GetInputPropertyFoldKey(index);
|
||||
_inputPropertyFoldables[key] = new InputPropertyFoldableContext(propertyFoldable, baseTitle);
|
||||
propertyFoldable.FoldingChanged += OnSectionFoldingChanged;
|
||||
sectionContainer.AddChild(propertyFoldable);
|
||||
propertyFoldable.AddChild(container);
|
||||
|
||||
var headerRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
container.AddChild(headerRow);
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Text = propInfo.Label,
|
||||
CustomMinimumSize = new Vector2(60, 0),
|
||||
};
|
||||
|
||||
nameLabel.AddThemeColorOverride("font_color", _inputPropertyColor);
|
||||
headerRow.AddChild(nameLabel);
|
||||
|
||||
List<Func<NodeEditorProperty>> resolverFactories =
|
||||
StatescriptResolverRegistry.GetCompatibleFactories(propInfo.ExpectedType);
|
||||
|
||||
if (propInfo.IsArray)
|
||||
{
|
||||
resolverFactories.RemoveAll(factory =>
|
||||
{
|
||||
return StatescriptResolverRegistry.GetResolverTypeId(factory) != "ArrayVariable";
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
resolverFactories.RemoveAll(factory =>
|
||||
{
|
||||
return StatescriptResolverRegistry.GetResolverTypeId(factory) == "ArrayVariable";
|
||||
});
|
||||
}
|
||||
|
||||
if (resolverFactories.Count == 0)
|
||||
{
|
||||
var errorLabel = new Label
|
||||
@@ -49,7 +69,8 @@ public partial class StatescriptGraphNode
|
||||
};
|
||||
|
||||
errorLabel.AddThemeColorOverride("font_color", Colors.Red);
|
||||
headerRow.AddChild(errorLabel);
|
||||
container.AddChild(errorLabel);
|
||||
UpdateInputPropertyFoldableTitle(key);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,20 +82,18 @@ public partial class StatescriptGraphNode
|
||||
|
||||
foreach (Func<NodeEditorProperty> factory in resolverFactories)
|
||||
{
|
||||
using NodeEditorProperty temp = factory();
|
||||
resolverDropdown.AddItem(temp.DisplayName);
|
||||
resolverDropdown.AddItem(StatescriptResolverRegistry.GetDisplayName(factory));
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? binding = FindBinding(StatescriptPropertyDirection.Input, index);
|
||||
var selectedIndex = 0;
|
||||
int selectedIndex = 0;
|
||||
|
||||
if (binding?.Resolver is not null)
|
||||
{
|
||||
for (var i = 0; i < resolverFactories.Count; i++)
|
||||
for (int i = 0; i < resolverFactories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = resolverFactories[i]();
|
||||
|
||||
if (temp.ResolverTypeId == GetResolverTypeId(binding.Resolver))
|
||||
if (StatescriptResolverRegistry.GetResolverTypeId(resolverFactories[i])
|
||||
== GetResolverTypeId(binding.Resolver))
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
@@ -83,16 +102,7 @@ public partial class StatescriptGraphNode
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < resolverFactories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = resolverFactories[i]();
|
||||
|
||||
if (temp.ResolverTypeId == "Variant")
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
selectedIndex = StatescriptResolverRegistry.GetDefaultFactoryIndex(resolverFactories, propInfo.IsArray);
|
||||
}
|
||||
|
||||
resolverDropdown.Selected = selectedIndex;
|
||||
@@ -101,7 +111,6 @@ public partial class StatescriptGraphNode
|
||||
var editorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
container.AddChild(editorContainer);
|
||||
|
||||
var key = new PropertySlotKey(StatescriptPropertyDirection.Input, index);
|
||||
_inputPropertyContexts[key] = new InputPropertyContext(resolverFactories, propInfo, editorContainer);
|
||||
|
||||
ShowResolverEditorUI(
|
||||
@@ -113,8 +122,11 @@ public partial class StatescriptGraphNode
|
||||
index,
|
||||
propInfo.IsArray);
|
||||
|
||||
var capturedIndex = index;
|
||||
resolverDropdown.ItemSelected += selectedItem => OnInputResolverDropdownItemSelected(selectedItem, capturedIndex);
|
||||
UpdateInputPropertyFoldableTitle(key);
|
||||
|
||||
int capturedIndex = index;
|
||||
resolverDropdown.ItemSelected +=
|
||||
selectedItem => OnInputResolverDropdownItemSelected(selectedItem, capturedIndex);
|
||||
}
|
||||
|
||||
private void OnInputResolverDropdownItemSelected(long x, int index)
|
||||
@@ -155,24 +167,29 @@ public partial class StatescriptGraphNode
|
||||
SaveResolverEditor(editor, StatescriptPropertyDirection.Input, index);
|
||||
}
|
||||
|
||||
UpdateInputPropertyFoldableTitle(key);
|
||||
|
||||
StatescriptNodeProperty? updated = FindBinding(StatescriptPropertyDirection.Input, index);
|
||||
var newResolver = updated?.Resolver?.Duplicate() as StatescriptResolverResource;
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Resolver Type", customContext: _graph);
|
||||
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
newResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(newResolver));
|
||||
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Input,
|
||||
index,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(oldResolver));
|
||||
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
@@ -212,12 +229,12 @@ public partial class StatescriptGraphNode
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? binding = FindBinding(StatescriptPropertyDirection.Output, index);
|
||||
var selectedIndex = 0;
|
||||
int selectedIndex = 0;
|
||||
|
||||
if (binding?.Resolver is VariableResolverResource varRes
|
||||
&& !string.IsNullOrEmpty(varRes.VariableName))
|
||||
{
|
||||
for (var i = 0; i < _graph.Variables.Count; i++)
|
||||
for (int i = 0; i < _graph.Variables.Count; i++)
|
||||
{
|
||||
if (_graph.Variables[i].VariableName == varRes.VariableName)
|
||||
{
|
||||
@@ -233,14 +250,15 @@ public partial class StatescriptGraphNode
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
var variableName = _graph.Variables[selectedIndex].VariableName;
|
||||
string variableName = _graph.Variables[selectedIndex].VariableName;
|
||||
EnsureBinding(StatescriptPropertyDirection.Output, index).Resolver =
|
||||
new VariableResolverResource { VariableName = variableName };
|
||||
}
|
||||
}
|
||||
|
||||
var capturedIndex = index;
|
||||
variableDropdown.ItemSelected += selectedItem => OnOutputVariableDropdownItemSelected(selectedItem, capturedIndex);
|
||||
int capturedIndex = index;
|
||||
variableDropdown.ItemSelected +=
|
||||
selectedItem => OnOutputVariableDropdownItemSelected(selectedItem, capturedIndex);
|
||||
|
||||
hBox.AddChild(variableDropdown);
|
||||
}
|
||||
@@ -255,25 +273,28 @@ public partial class StatescriptGraphNode
|
||||
var oldResolver = FindBinding(StatescriptPropertyDirection.Output, index)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
|
||||
var variableName = _graph.Variables[(int)x].VariableName;
|
||||
string variableName = _graph.Variables[(int)x].VariableName;
|
||||
var newResolver = new VariableResolverResource { VariableName = variableName };
|
||||
EnsureBinding(StatescriptPropertyDirection.Output, index).Resolver = newResolver;
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Output Variable", customContext: _graph);
|
||||
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Output,
|
||||
index,
|
||||
(StatescriptResolverResource)newResolver.Duplicate());
|
||||
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)StatescriptPropertyDirection.Output,
|
||||
index,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(oldResolver));
|
||||
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
@@ -295,6 +316,7 @@ public partial class StatescriptGraphNode
|
||||
}
|
||||
|
||||
NodeEditorProperty resolverEditor = factory();
|
||||
resolverEditor.ConfigureAllowedExpectedTypes(expectedType);
|
||||
|
||||
var key = new PropertySlotKey(direction, propertyIndex);
|
||||
|
||||
@@ -333,18 +355,21 @@ public partial class StatescriptGraphNode
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Change Node Property", customContext: _graph);
|
||||
|
||||
_undoRedo.AddDoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
newResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(newResolver));
|
||||
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(oldResolver));
|
||||
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
{
|
||||
private const string FoldInputKey = "_fold_input";
|
||||
private const string FoldOutputKey = "_fold_output";
|
||||
private const string FoldInputPropertyKeyPrefix = "_fold_input_property_";
|
||||
private const string CustomWidthKey = "_custom_width";
|
||||
|
||||
private static readonly Color _entryColor = new(0x2a4a8dff);
|
||||
@@ -34,6 +35,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
|
||||
private readonly Dictionary<PropertySlotKey, NodeEditorProperty> _activeResolverEditors = [];
|
||||
private readonly Dictionary<FoldableContainer, string> _foldableKeys = [];
|
||||
private readonly Dictionary<PropertySlotKey, InputPropertyFoldableContext> _inputPropertyFoldables = [];
|
||||
|
||||
private StatescriptNodeDiscovery.NodeTypeInfo? _typeInfo;
|
||||
private StatescriptGraph? _graph;
|
||||
@@ -42,6 +44,8 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
private bool _resizeConnected;
|
||||
private float _widthBeforeResize;
|
||||
private string? _highlightedVariableName;
|
||||
private string? _highlightedSharedVariableSetPath;
|
||||
private string? _highlightedSharedVariableName;
|
||||
private bool _isHighlighted;
|
||||
|
||||
/// <summary>
|
||||
@@ -79,9 +83,14 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
public void SetHighlightedVariable(string? variableName)
|
||||
{
|
||||
_highlightedVariableName = variableName;
|
||||
_isHighlighted = !string.IsNullOrEmpty(variableName) && ReferencesVariable(variableName!);
|
||||
ApplyHighlightBorder();
|
||||
UpdateChildHighlights();
|
||||
RefreshHighlightState();
|
||||
}
|
||||
|
||||
public void SetHighlightedSharedVariable(string? sharedVariableSetPath, string? variableName)
|
||||
{
|
||||
_highlightedSharedVariableSetPath = sharedVariableSetPath;
|
||||
_highlightedSharedVariableName = variableName;
|
||||
RefreshHighlightState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,6 +104,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
_graph = graph;
|
||||
_activeResolverEditors.Clear();
|
||||
_foldableKeys.Clear();
|
||||
_inputPropertyFoldables.Clear();
|
||||
|
||||
Name = resource.NodeId;
|
||||
Title = resource.Title;
|
||||
@@ -119,6 +129,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
{
|
||||
SetupNodeByType(resource.NodeType);
|
||||
ApplyBottomPadding();
|
||||
RefreshHighlightState();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,12 +144,14 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
}
|
||||
|
||||
ApplyBottomPadding();
|
||||
RefreshHighlightState();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
_inputPropertyContexts.Clear();
|
||||
_foldableKeys.Clear();
|
||||
_inputPropertyFoldables.Clear();
|
||||
|
||||
_activeCustomEditor?.Unbind();
|
||||
_activeCustomEditor = null;
|
||||
@@ -187,6 +200,16 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
return GetFoldState(key);
|
||||
}
|
||||
|
||||
internal bool GetFoldStateInternal(string key, bool defaultValue)
|
||||
{
|
||||
return GetFoldState(key, defaultValue);
|
||||
}
|
||||
|
||||
internal void SetFoldStateWithUndoInternal(string key, bool folded)
|
||||
{
|
||||
SetFoldStateWithUndo(key, folded);
|
||||
}
|
||||
|
||||
internal StatescriptNodeProperty? FindBindingInternal(
|
||||
StatescriptPropertyDirection direction,
|
||||
int propertyIndex)
|
||||
@@ -226,13 +249,15 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
newResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(newResolver));
|
||||
|
||||
_undoRedo.AddUndoMethod(
|
||||
this,
|
||||
MethodName.ApplyResolverBinding,
|
||||
(int)direction,
|
||||
propertyIndex,
|
||||
oldResolver ?? new StatescriptResolverResource());
|
||||
Variant.From(oldResolver));
|
||||
|
||||
_undoRedo.CommitAction(false);
|
||||
}
|
||||
|
||||
@@ -253,6 +278,11 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
PropertyBindingChanged?.Invoke();
|
||||
}
|
||||
|
||||
internal void UpdateInputPropertyFoldableTitlesInternal()
|
||||
{
|
||||
UpdateInputPropertyFoldableTitles();
|
||||
}
|
||||
|
||||
private static string GetResolverTypeId(StatescriptResolverResource resolver)
|
||||
{
|
||||
return resolver.ResolverTypeId;
|
||||
@@ -267,11 +297,16 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetInputPropertyFoldKey(int propertyIndex)
|
||||
{
|
||||
return $"{FoldInputPropertyKeyPrefix}{propertyIndex}";
|
||||
}
|
||||
|
||||
private void SetupFromTypeInfo(StatescriptNodeDiscovery.NodeTypeInfo typeInfo)
|
||||
{
|
||||
var maxSlots = Math.Max(typeInfo.InputPortLabels.Length, typeInfo.OutputPortLabels.Length);
|
||||
int maxSlots = Math.Max(typeInfo.InputPortLabels.Length, typeInfo.OutputPortLabels.Length);
|
||||
|
||||
for (var slot = 0; slot < maxSlots; slot++)
|
||||
for (int slot = 0; slot < maxSlots; slot++)
|
||||
{
|
||||
var hBox = new HBoxContainer();
|
||||
hBox.AddThemeConstantOverride("separation", 16);
|
||||
@@ -342,14 +377,14 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
{
|
||||
if (typeInfo.InputPropertiesInfo.Length > 0)
|
||||
{
|
||||
var folded = GetFoldState(FoldInputKey);
|
||||
bool folded = GetFoldState(FoldInputKey);
|
||||
FoldableContainer inputContainer = AddPropertySectionDivider(
|
||||
"Input Properties",
|
||||
_inputPropertyColor,
|
||||
FoldInputKey,
|
||||
folded);
|
||||
|
||||
for (var i = 0; i < typeInfo.InputPropertiesInfo.Length; i++)
|
||||
for (int i = 0; i < typeInfo.InputPropertiesInfo.Length; i++)
|
||||
{
|
||||
AddInputPropertyRow(typeInfo.InputPropertiesInfo[i], i, inputContainer);
|
||||
}
|
||||
@@ -357,14 +392,14 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
|
||||
if (typeInfo.OutputVariablesInfo.Length > 0)
|
||||
{
|
||||
var folded = GetFoldState(FoldOutputKey);
|
||||
bool folded = GetFoldState(FoldOutputKey);
|
||||
FoldableContainer outputContainer = AddPropertySectionDivider(
|
||||
"Output Variables",
|
||||
_outputVariableColor,
|
||||
FoldOutputKey,
|
||||
folded);
|
||||
|
||||
for (var i = 0; i < typeInfo.OutputVariablesInfo.Length; i++)
|
||||
for (int i = 0; i < typeInfo.OutputVariablesInfo.Length; i++)
|
||||
{
|
||||
AddOutputVariableRow(typeInfo.OutputVariablesInfo[i], i, outputContainer);
|
||||
}
|
||||
@@ -384,6 +419,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
{
|
||||
Title = sectionTitle,
|
||||
Folded = folded,
|
||||
CustomMinimumSize = new Vector2(192, 0),
|
||||
};
|
||||
|
||||
sectionContainer.AddThemeColorOverride("font_color", color);
|
||||
@@ -400,24 +436,52 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
{
|
||||
foreach (KeyValuePair<FoldableContainer, string> kvp in _foldableKeys.Where(kvp => IsInstanceValid(kvp.Key)))
|
||||
{
|
||||
var stored = GetFoldState(kvp.Value);
|
||||
bool stored = GetFoldState(kvp.Value);
|
||||
if (kvp.Key.Folded != stored)
|
||||
{
|
||||
SetFoldStateWithUndo(kvp.Value, kvp.Key.Folded);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateInputPropertyFoldableTitles();
|
||||
RefreshHighlightState();
|
||||
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void UpdateInputPropertyFoldableTitle(PropertySlotKey key)
|
||||
{
|
||||
if (!_inputPropertyFoldables.TryGetValue(key, out InputPropertyFoldableContext? context)
|
||||
|| !IsInstanceValid(context.Foldable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeResolverEditors.TryGetValue(key, out NodeEditorProperty? editor);
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle(context.BaseTitle, context.Foldable, editor);
|
||||
}
|
||||
|
||||
private void UpdateInputPropertyFoldableTitles()
|
||||
{
|
||||
foreach (PropertySlotKey key in _inputPropertyFoldables.Keys.ToArray())
|
||||
{
|
||||
UpdateInputPropertyFoldableTitle(key);
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetFoldState(string key)
|
||||
{
|
||||
return GetFoldState(key, false);
|
||||
}
|
||||
|
||||
private bool GetFoldState(string key, bool defaultValue)
|
||||
{
|
||||
if (NodeResource is not null && NodeResource.CustomData.TryGetValue(key, out Variant value))
|
||||
{
|
||||
return value.AsBool();
|
||||
}
|
||||
|
||||
return false;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private void SetFoldState(string key, bool folded)
|
||||
@@ -437,7 +501,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
return;
|
||||
}
|
||||
|
||||
var oldFolded = GetFoldState(key);
|
||||
bool oldFolded = GetFoldState(key);
|
||||
|
||||
if (oldFolded == folded)
|
||||
{
|
||||
@@ -478,12 +542,12 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
|
||||
private void OnResizeEnd(Vector2 newSize)
|
||||
{
|
||||
var newWidth = CustomMinimumSize.X;
|
||||
float newWidth = CustomMinimumSize.X;
|
||||
|
||||
if (_undoRedo is not null && NodeResource is not null
|
||||
&& !Mathf.IsEqualApprox(_widthBeforeResize, newWidth))
|
||||
{
|
||||
var oldWidth = _widthBeforeResize;
|
||||
float oldWidth = _widthBeforeResize;
|
||||
|
||||
_undoRedo.CreateAction("Resize Node", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(
|
||||
@@ -512,7 +576,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
if (NodeResource is not null
|
||||
&& NodeResource.CustomData.TryGetValue(CustomWidthKey, out Variant value))
|
||||
{
|
||||
var width = (float)value.AsDouble();
|
||||
float width = (float)value.AsDouble();
|
||||
|
||||
if (width > 0)
|
||||
{
|
||||
@@ -534,7 +598,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
private void ApplyResolverBinding(
|
||||
int directionInt,
|
||||
int propertyIndex,
|
||||
StatescriptResolverResource resolver)
|
||||
Variant resolverVariant)
|
||||
{
|
||||
if (NodeResource is null)
|
||||
{
|
||||
@@ -543,7 +607,9 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
|
||||
var direction = (StatescriptPropertyDirection)directionInt;
|
||||
StatescriptNodeProperty binding = EnsureBinding(direction, propertyIndex);
|
||||
binding.Resolver = resolver;
|
||||
binding.Resolver = resolverVariant.VariantType == Variant.Type.Nil
|
||||
? null
|
||||
: resolverVariant.AsGodotObject() as StatescriptResolverResource;
|
||||
RebuildNode();
|
||||
}
|
||||
|
||||
@@ -560,6 +626,14 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
Size = new Vector2(Size.X, 0);
|
||||
}
|
||||
|
||||
private void RefreshHighlightState()
|
||||
{
|
||||
_isHighlighted = (!string.IsNullOrEmpty(_highlightedVariableName) && ReferencesVariable(_highlightedVariableName))
|
||||
|| ReferencesSharedVariable(_highlightedSharedVariableSetPath, _highlightedSharedVariableName);
|
||||
ApplyHighlightBorder();
|
||||
UpdateChildHighlights();
|
||||
}
|
||||
|
||||
private StatescriptNodeProperty? FindBinding(
|
||||
StatescriptPropertyDirection direction,
|
||||
int propertyIndex)
|
||||
@@ -607,7 +681,7 @@ public partial class StatescriptGraphNode : GraphNode, ISerializationListener
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = NodeResource.PropertyBindings.Count - 1; i >= 0; i--)
|
||||
for (int i = NodeResource.PropertyBindings.Count - 1; i >= 0; i--)
|
||||
{
|
||||
StatescriptNodeProperty binding = NodeResource.PropertyBindings[i];
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ internal static class StatescriptNodeDiscovery
|
||||
{
|
||||
IReadOnlyList<NodeTypeInfo> types = GetDiscoveredNodeTypes();
|
||||
|
||||
for (var i = 0; i < types.Count; i++)
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
if (types[i].RuntimeTypeName == runtimeTypeName)
|
||||
{
|
||||
@@ -131,11 +131,11 @@ internal static class StatescriptNodeDiscovery
|
||||
|
||||
private static NodeTypeInfo BuildNodeTypeInfo(Type type, StatescriptNodeType nodeType)
|
||||
{
|
||||
var displayName = FormatDisplayName(type.Name);
|
||||
var runtimeTypeName = type.FullName!;
|
||||
string displayName = FormatDisplayName(type.Name);
|
||||
string runtimeTypeName = type.FullName!;
|
||||
|
||||
// Get constructor parameter names.
|
||||
var constructorParamNames = GetConstructorParameterNames(type);
|
||||
string[] constructorParamNames = GetConstructorParameterNames(type);
|
||||
|
||||
// Determine ports and description by instantiating a temporary node.
|
||||
string[] inputLabels;
|
||||
@@ -194,8 +194,8 @@ internal static class StatescriptNodeDiscovery
|
||||
ConstructorInfo constructor = constructors.OrderBy(x => x.GetParameters().Length).First();
|
||||
ParameterInfo[] parameters = constructor.GetParameters();
|
||||
|
||||
var args = new object[parameters.Length];
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
object[] args = new object[parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
Type paramType = parameters[i].ParameterType;
|
||||
|
||||
@@ -236,8 +236,8 @@ internal static class StatescriptNodeDiscovery
|
||||
|
||||
private static string[] GetInputPortLabels(Node node, StatescriptNodeType nodeType)
|
||||
{
|
||||
var count = node.InputPorts.Length;
|
||||
var labels = new string[count];
|
||||
int count = node.InputPorts.Length;
|
||||
string[] labels = new string[count];
|
||||
|
||||
switch (nodeType)
|
||||
{
|
||||
@@ -268,7 +268,7 @@ internal static class StatescriptNodeDiscovery
|
||||
labels[1] = "Abort";
|
||||
}
|
||||
|
||||
for (var i = 2; i < count; i++)
|
||||
for (int i = 2; i < count; i++)
|
||||
{
|
||||
labels[i] = $"Input {i}";
|
||||
}
|
||||
@@ -276,7 +276,7 @@ internal static class StatescriptNodeDiscovery
|
||||
break;
|
||||
|
||||
default:
|
||||
for (var i = 0; i < count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
labels[i] = $"Input {i}";
|
||||
}
|
||||
@@ -289,8 +289,8 @@ internal static class StatescriptNodeDiscovery
|
||||
|
||||
private static string[] GetOutputPortLabels(Node node, StatescriptNodeType nodeType)
|
||||
{
|
||||
var count = node.OutputPorts.Length;
|
||||
var labels = new string[count];
|
||||
int count = node.OutputPorts.Length;
|
||||
string[] labels = new string[count];
|
||||
|
||||
switch (nodeType)
|
||||
{
|
||||
@@ -336,7 +336,7 @@ internal static class StatescriptNodeDiscovery
|
||||
labels[3] = "Subgraph";
|
||||
}
|
||||
|
||||
for (var i = 4; i < count; i++)
|
||||
for (int i = 4; i < count; i++)
|
||||
{
|
||||
labels[i] = $"Event {i}";
|
||||
}
|
||||
@@ -344,7 +344,7 @@ internal static class StatescriptNodeDiscovery
|
||||
break;
|
||||
|
||||
default:
|
||||
for (var i = 0; i < count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
labels[i] = $"Output {i}";
|
||||
}
|
||||
@@ -357,10 +357,10 @@ internal static class StatescriptNodeDiscovery
|
||||
|
||||
private static bool[] GetSubgraphFlags(Node node)
|
||||
{
|
||||
var count = node.OutputPorts.Length;
|
||||
var flags = new bool[count];
|
||||
int count = node.OutputPorts.Length;
|
||||
bool[] flags = new bool[count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
flags[i] = node.OutputPorts[i] is SubgraphPort;
|
||||
}
|
||||
@@ -371,7 +371,7 @@ internal static class StatescriptNodeDiscovery
|
||||
private static InputPropertyInfo[] GetInputPropertiesInfo(Node node)
|
||||
{
|
||||
var propertiesInfo = new InputPropertyInfo[node.InputProperties.Length];
|
||||
for (var i = 0; i < node.InputProperties.Length; i++)
|
||||
for (int i = 0; i < node.InputProperties.Length; i++)
|
||||
{
|
||||
propertiesInfo[i] = new InputPropertyInfo(
|
||||
node.InputProperties[i].Label,
|
||||
@@ -384,7 +384,7 @@ internal static class StatescriptNodeDiscovery
|
||||
private static OutputVariableInfo[] GetOutputVariablesInfo(Node node)
|
||||
{
|
||||
var variablesInfo = new OutputVariableInfo[node.OutputVariables.Length];
|
||||
for (var i = 0; i < node.OutputVariables.Length; i++)
|
||||
for (int i = 0; i < node.OutputVariables.Length; i++)
|
||||
{
|
||||
variablesInfo[i] = new OutputVariableInfo(
|
||||
node.OutputVariables[i].Label,
|
||||
@@ -425,7 +425,7 @@ internal static class StatescriptNodeDiscovery
|
||||
|
||||
// Insert spaces before capital letters for camelCase names.
|
||||
var result = new System.Text.StringBuilder();
|
||||
for (var i = 0; i < typeName.Length; i++)
|
||||
for (int i = 0; i < typeName.Length; i++)
|
||||
{
|
||||
if (i > 0 && char.IsUpper(typeName[i]) && !char.IsUpper(typeName[i - 1]))
|
||||
{
|
||||
|
||||
@@ -36,19 +36,61 @@ internal static class StatescriptResolverRegistry
|
||||
/// <returns>A list of compatible resolver editor factories.</returns>
|
||||
public static List<Func<NodeEditorProperty>> GetCompatibleFactories(Type expectedType)
|
||||
{
|
||||
var result = new List<Func<NodeEditorProperty>>();
|
||||
return [.. _factories.Where(factory => IsCompatibleFactory(factory, expectedType))];
|
||||
}
|
||||
|
||||
foreach (Func<NodeEditorProperty> factory in _factories)
|
||||
public static int GetDefaultFactoryIndex(List<Func<NodeEditorProperty>> factories, bool isArray)
|
||||
{
|
||||
for (int i = 0; i < factories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = factory();
|
||||
|
||||
if (temp.IsCompatibleWith(expectedType))
|
||||
if (isArray)
|
||||
{
|
||||
result.Add(factory);
|
||||
if (GetResolverTypeId(factories[i]) == "ArrayVariable")
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else if (GetResolverTypeId(factories[i]) == "Variant")
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static string GetDisplayName(Func<NodeEditorProperty> factory)
|
||||
{
|
||||
return UseTemporaryEditor(factory, static editor => editor.DisplayName);
|
||||
}
|
||||
|
||||
public static string GetResolverTypeId(Func<NodeEditorProperty> factory)
|
||||
{
|
||||
return UseTemporaryEditor(factory, static editor => editor.ResolverTypeId);
|
||||
}
|
||||
|
||||
public static bool IsCompatibleFactory(Func<NodeEditorProperty> factory, Type expectedType)
|
||||
{
|
||||
return UseTemporaryEditor(factory, editor => editor.IsCompatibleWith(expectedType));
|
||||
}
|
||||
|
||||
private static TResult UseTemporaryEditor<TResult>(
|
||||
Func<NodeEditorProperty> factory,
|
||||
Func<NodeEditorProperty, TResult> selector)
|
||||
{
|
||||
NodeEditorProperty editor = factory();
|
||||
|
||||
try
|
||||
{
|
||||
return selector(editor);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (global::Godot.GodotObject.IsInstanceValid(editor))
|
||||
{
|
||||
editor.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -68,6 +68,12 @@ internal sealed partial class StatescriptVariablePanel
|
||||
|
||||
private void DoRemoveVariable(StatescriptGraph graph, StatescriptGraphVariable variable, int index)
|
||||
{
|
||||
if (_selectedVariableName == variable.VariableName)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
VariableHighlightChanged?.Invoke(null);
|
||||
}
|
||||
|
||||
graph.Variables.RemoveAt(index);
|
||||
ClearReferencesToVariable(variable.VariableName);
|
||||
RebuildList();
|
||||
|
||||
@@ -73,7 +73,7 @@ internal sealed partial class StatescriptVariablePanel
|
||||
var headerRow = new HBoxContainer();
|
||||
vBox.AddChild(headerRow);
|
||||
|
||||
var isExpanded = _expandedArrays.Contains(variable.VariableName);
|
||||
bool isExpanded = _expandedArrays.Contains(variable.VariableName);
|
||||
|
||||
var elementsContainer = new VBoxContainer
|
||||
{
|
||||
@@ -93,7 +93,7 @@ internal sealed partial class StatescriptVariablePanel
|
||||
{
|
||||
elementsContainer.Visible = x;
|
||||
|
||||
var wasExpanded = !x;
|
||||
bool wasExpanded = !x;
|
||||
|
||||
if (x)
|
||||
{
|
||||
@@ -162,9 +162,9 @@ internal sealed partial class StatescriptVariablePanel
|
||||
|
||||
vBox.AddChild(elementsContainer);
|
||||
|
||||
for (var i = 0; i < variable.InitialArrayValues.Count; i++)
|
||||
for (int i = 0; i < variable.InitialArrayValues.Count; i++)
|
||||
{
|
||||
var capturedIndex = i;
|
||||
int capturedIndex = i;
|
||||
|
||||
if (variable.VariableType == StatescriptVariableType.Bool)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISerializationListener
|
||||
{
|
||||
private const string ExpandedArraysMetaKey = "_expanded_arrays";
|
||||
private const string HeaderRowNodeName = "HeaderRow";
|
||||
private const string AddButtonNodeName = "AddButton";
|
||||
private const string VariablesScrollNodeName = "VariablesScroll";
|
||||
private const string VariableListNodeName = "VariableList";
|
||||
private const string VariableNameButtonMetaKey = "_variable_name_button";
|
||||
|
||||
private static readonly Color _variableColor = new(0xe5c07bff);
|
||||
private static readonly Color _highlightColor = new(0x56b6c2ff);
|
||||
@@ -64,7 +69,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
SizeFlagsVertical = SizeFlags.ExpandFill;
|
||||
CustomMinimumSize = new Vector2(360, 0);
|
||||
|
||||
var headerHBox = new HBoxContainer();
|
||||
var headerHBox = new HBoxContainer { Name = HeaderRowNodeName };
|
||||
AddChild(headerHBox);
|
||||
|
||||
var titleLabel = new Label
|
||||
@@ -77,6 +82,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
_addButton = new Button
|
||||
{
|
||||
Name = AddButtonNodeName,
|
||||
Icon = _addIcon,
|
||||
Flat = true,
|
||||
TooltipText = "Add Variable",
|
||||
@@ -91,6 +97,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
var scrollContainer = new ScrollContainer
|
||||
{
|
||||
Name = VariablesScrollNodeName,
|
||||
SizeFlagsVertical = SizeFlags.ExpandFill,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
@@ -99,6 +106,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
_variableList = new VBoxContainer
|
||||
{
|
||||
Name = VariableListNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
|
||||
@@ -108,45 +116,19 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
public override void _ExitTree()
|
||||
{
|
||||
base._ExitTree();
|
||||
|
||||
if (_addButton is not null)
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
_creationDialog?.QueueFree();
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
ReleaseUiState();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
if (_variableList is not null)
|
||||
{
|
||||
foreach (Node child in _variableList.GetChildren())
|
||||
{
|
||||
_variableList.RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
_creationDialog?.Free();
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
ReleaseUiState();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
_addButton.Pressed += OnAddPressed;
|
||||
}
|
||||
@@ -165,6 +147,31 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
RebuildList();
|
||||
}
|
||||
|
||||
public string? GetSelectedVariableName()
|
||||
{
|
||||
return _selectedVariableName;
|
||||
}
|
||||
|
||||
public void RestoreSelectedVariable(string? variableName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(variableName) || !HasVariableNamed(variableName))
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
VariableHighlightChanged?.Invoke(_selectedVariableName);
|
||||
}
|
||||
|
||||
public void ClearSelectedVariable()
|
||||
{
|
||||
RestoreSelectedVariable(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="EditorUndoRedoManager"/> used for undo/redo support.
|
||||
/// </summary>
|
||||
@@ -179,26 +186,95 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
/// </summary>
|
||||
public void RebuildList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
if (_graph is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _graph.Variables.Count; i++)
|
||||
{
|
||||
AddVariableRow(_graph.Variables[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
private static int FindTypeDropdownIndex(OptionButton dropdown, StatescriptVariableType variableType)
|
||||
{
|
||||
for (int i = 0; i < dropdown.ItemCount; i++)
|
||||
{
|
||||
if (dropdown.GetItemId(i) == (int)variableType)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void UpdateVariableNameButtonAppearance(Button button, bool isSelected)
|
||||
{
|
||||
Color buttonColor = isSelected ? _highlightColor : _variableColor;
|
||||
button.AddThemeColorOverride("font_color", buttonColor);
|
||||
button.AddThemeColorOverride("font_pressed_color", _highlightColor);
|
||||
button.AddThemeColorOverride("font_hover_color", buttonColor.Lightened(0.2f));
|
||||
button.AddThemeColorOverride("font_hover_pressed_color", _highlightColor.Lightened(0.2f));
|
||||
}
|
||||
|
||||
private void EnsureControlsCached()
|
||||
{
|
||||
_addButton ??= GetNodeOrNull<Button>($"{HeaderRowNodeName}/{AddButtonNodeName}");
|
||||
_variableList ??= GetNodeOrNull<VBoxContainer>($"{VariablesScrollNodeName}/{VariableListNodeName}");
|
||||
}
|
||||
|
||||
private void ClearVariableList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null || !IsInstanceValid(_variableList))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Node child in _variableList.GetChildren())
|
||||
{
|
||||
_variableList.RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
if (_graph is null)
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
return;
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _graph.Variables.Count; i++)
|
||||
ClearVariableList();
|
||||
|
||||
if (_creationDialog is not null && IsInstanceValid(_creationDialog))
|
||||
{
|
||||
AddVariableRow(_graph.Variables[i], i);
|
||||
if (_creationDialog is AcceptDialog acceptDialog)
|
||||
{
|
||||
acceptDialog.Confirmed -= OnCreationConfirmed;
|
||||
}
|
||||
|
||||
_creationDialog.Free();
|
||||
}
|
||||
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
_variableList = null;
|
||||
_addButton = null;
|
||||
}
|
||||
|
||||
private void SaveExpandedArrayState()
|
||||
@@ -208,7 +284,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
return;
|
||||
}
|
||||
|
||||
var packed = new string[_expandedArrays.Count];
|
||||
string[] packed = new string[_expandedArrays.Count];
|
||||
_expandedArrays.CopyTo(packed);
|
||||
_graph.SetMeta(ExpandedArraysMetaKey, Variant.From(packed));
|
||||
}
|
||||
@@ -226,7 +302,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
if (meta.VariantType == Variant.Type.PackedStringArray)
|
||||
{
|
||||
foreach (var name in meta.AsStringArray())
|
||||
foreach (string name in meta.AsStringArray())
|
||||
{
|
||||
_expandedArrays.Add(name);
|
||||
}
|
||||
@@ -254,7 +330,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
rowContainer.AddChild(headerRow);
|
||||
|
||||
var isSelected = _selectedVariableName == variable.VariableName;
|
||||
bool isSelected = _selectedVariableName == variable.VariableName;
|
||||
|
||||
var nameButton = new Button
|
||||
{
|
||||
@@ -266,28 +342,15 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
Alignment = HorizontalAlignment.Left,
|
||||
};
|
||||
|
||||
Color buttonColor = isSelected ? _highlightColor : _variableColor;
|
||||
nameButton.AddThemeColorOverride("font_color", buttonColor);
|
||||
nameButton.AddThemeColorOverride("font_pressed_color", _highlightColor);
|
||||
nameButton.AddThemeColorOverride("font_hover_color", buttonColor.Lightened(0.2f));
|
||||
nameButton.AddThemeColorOverride("font_hover_pressed_color", _highlightColor.Lightened(0.2f));
|
||||
nameButton.SetMeta(VariableNameButtonMetaKey, variable.VariableName);
|
||||
UpdateVariableNameButtonAppearance(nameButton, isSelected);
|
||||
nameButton.AddThemeFontOverride(
|
||||
"font",
|
||||
EditorInterface.Singleton.GetEditorTheme().GetFont("bold", "EditorFonts"));
|
||||
|
||||
nameButton.Toggled += pressed =>
|
||||
{
|
||||
if (pressed)
|
||||
{
|
||||
_selectedVariableName = variable.VariableName;
|
||||
}
|
||||
else if (_selectedVariableName == variable.VariableName)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
RebuildList();
|
||||
VariableHighlightChanged?.Invoke(_selectedVariableName);
|
||||
SetSelectedVariable(variable.VariableName, pressed);
|
||||
};
|
||||
|
||||
headerRow.AddChild(nameButton);
|
||||
@@ -301,7 +364,7 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
typeLabel.AddThemeColorOverride("font_color", new Color(0.6f, 0.6f, 0.6f));
|
||||
headerRow.AddChild(typeLabel);
|
||||
|
||||
var capturedIndex = index;
|
||||
int capturedIndex = index;
|
||||
|
||||
var deleteButton = new Button
|
||||
{
|
||||
@@ -328,6 +391,47 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
rowContainer.AddChild(new HSeparator());
|
||||
}
|
||||
|
||||
private void SetSelectedVariable(string variableName, bool selected)
|
||||
{
|
||||
if (selected)
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
}
|
||||
else if (_selectedVariableName == variableName)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
VariableHighlightChanged?.Invoke(_selectedVariableName);
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisuals()
|
||||
{
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(_variableList);
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisualsRecursive(Node parent)
|
||||
{
|
||||
foreach (Node child in parent.GetChildren())
|
||||
{
|
||||
if (child is Button button && button.HasMeta(VariableNameButtonMetaKey))
|
||||
{
|
||||
string variableName = button.GetMeta(VariableNameButtonMetaKey).AsString();
|
||||
bool isSelected = _selectedVariableName == variableName;
|
||||
button.SetPressedNoSignal(isSelected);
|
||||
UpdateVariableNameButtonAppearance(button, isSelected);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAddPressed()
|
||||
{
|
||||
if (_graph is null)
|
||||
@@ -373,12 +477,14 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
|
||||
StatescriptVariableType[] allTypes = StatescriptVariableTypeConverter.GetAllTypes();
|
||||
|
||||
for (var t = 0; t < allTypes.Length; t++)
|
||||
for (int t = 0; t < allTypes.Length; t++)
|
||||
{
|
||||
_newTypeDropdown.AddItem(StatescriptVariableTypeConverter.GetDisplayName(allTypes[t]), t);
|
||||
_newTypeDropdown.AddItem(
|
||||
StatescriptVariableTypeConverter.GetDisplayName(allTypes[t]),
|
||||
(int)allTypes[t]);
|
||||
}
|
||||
|
||||
_newTypeDropdown.Selected = (int)StatescriptVariableType.Int;
|
||||
_newTypeDropdown.Selected = FindTypeDropdownIndex(_newTypeDropdown, StatescriptVariableType.Int);
|
||||
typeRow.AddChild(_newTypeDropdown);
|
||||
|
||||
var arrayRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
@@ -404,20 +510,20 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
return;
|
||||
}
|
||||
|
||||
var name = _newNameEdit.Text.Trim();
|
||||
string name = _newNameEdit.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(name) || HasVariableNamed(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedIndex = _newTypeDropdown.Selected;
|
||||
int selectedIndex = _newTypeDropdown.Selected;
|
||||
if (selectedIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedId = _newTypeDropdown.GetItemId(selectedIndex);
|
||||
int selectedId = _newTypeDropdown.GetItemId(selectedIndex);
|
||||
var varType = (StatescriptVariableType)selectedId;
|
||||
|
||||
var newVariable = new StatescriptGraphVariable
|
||||
@@ -431,8 +537,8 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Add Graph Variable", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(this, MethodName.DoAddVariable, _graph!, newVariable);
|
||||
_undoRedo.AddUndoMethod(this, MethodName.UndoAddVariable, _graph!, newVariable);
|
||||
_undoRedo.AddDoMethod(this, MethodName.DoAddVariable, _graph, newVariable);
|
||||
_undoRedo.AddUndoMethod(this, MethodName.UndoAddVariable, _graph, newVariable);
|
||||
_undoRedo.CommitAction();
|
||||
}
|
||||
else
|
||||
@@ -459,8 +565,8 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
_undoRedo.CreateAction("Remove Graph Variable", customContext: _graph);
|
||||
_undoRedo.AddDoMethod(this, MethodName.DoRemoveVariable, _graph!, variable, index);
|
||||
_undoRedo.AddUndoMethod(this, MethodName.UndoRemoveVariable, _graph!, variable, index);
|
||||
_undoRedo.AddDoMethod(this, MethodName.DoRemoveVariable, _graph, variable, index);
|
||||
_undoRedo.AddUndoMethod(this, MethodName.UndoRemoveVariable, _graph, variable, index);
|
||||
_undoRedo.CommitAction();
|
||||
}
|
||||
else
|
||||
@@ -497,8 +603,8 @@ internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISeriali
|
||||
}
|
||||
|
||||
const string baseName = "variable";
|
||||
var counter = 1;
|
||||
var name = baseName;
|
||||
int counter = 1;
|
||||
string name = baseName;
|
||||
|
||||
while (HasVariableNamed(name))
|
||||
{
|
||||
|
||||
196
addons/forge/editor/statescript/node_editors/DebugNodeEditor.cs
Normal file
196
addons/forge/editor/statescript/node_editors/DebugNodeEditor.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.NodeEditors;
|
||||
|
||||
/// <summary>
|
||||
/// Custom editor for <c>DebugNode</c>. Lets the user choose the concrete value type to debug, then constrains the
|
||||
/// source resolver picker to that type so debug output can be interpreted correctly.
|
||||
/// </summary>
|
||||
[Tool]
|
||||
internal sealed partial class DebugNodeEditor : CustomNodeEditor
|
||||
{
|
||||
private const string ValueTypeKey = "valueType";
|
||||
private const string TypeFoldKey = "_debug_type_fold";
|
||||
|
||||
private StatescriptVariableType _selectedType = StatescriptVariableType.Int;
|
||||
private VBoxContainer? _inputRootContainer;
|
||||
private VBoxContainer? _inputEditorContainer;
|
||||
private StatescriptNodeDiscovery.NodeTypeInfo? _cachedTypeInfo;
|
||||
private OptionButton? _typeDropdown;
|
||||
private FoldableContainer? _typeFoldable;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HandledRuntimeTypeName => "Gamesmiths.Forge.Statescript.Nodes.Action.DebugNode";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void BuildPropertySections(StatescriptNodeDiscovery.NodeTypeInfo typeInfo)
|
||||
{
|
||||
_cachedTypeInfo = typeInfo;
|
||||
|
||||
if (NodeResource.CustomData.TryGetValue(ValueTypeKey, out Variant storedType))
|
||||
{
|
||||
_selectedType = (StatescriptVariableType)storedType.AsInt32();
|
||||
}
|
||||
|
||||
bool inputFolded = GetFoldState("_fold_input");
|
||||
FoldableContainer inputContainer = AddPropertySectionDivider(
|
||||
"Input Properties",
|
||||
InputPropertyColor,
|
||||
"_fold_input",
|
||||
inputFolded);
|
||||
|
||||
_inputRootContainer = new VBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
inputContainer.AddChild(_inputRootContainer);
|
||||
|
||||
BuildTypeRow(_inputRootContainer);
|
||||
|
||||
if (typeInfo.InputPropertiesInfo.Length > 0)
|
||||
{
|
||||
RebuildInputEditor(typeInfo.InputPropertiesInfo[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTypeOption(OptionButton dropdown, StatescriptVariableType type, string label)
|
||||
{
|
||||
dropdown.AddItem(label, (int)type);
|
||||
}
|
||||
|
||||
private void BuildTypeRow(VBoxContainer root)
|
||||
{
|
||||
_typeFoldable = new FoldableContainer
|
||||
{
|
||||
Title = "Type:",
|
||||
Folded = GetFoldState(TypeFoldKey, true),
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
};
|
||||
_typeFoldable.FoldingChanged += OnTypeFoldableFoldingChanged;
|
||||
root.AddChild(_typeFoldable);
|
||||
|
||||
var container = new VBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
_typeFoldable.AddChild(container);
|
||||
|
||||
var headerRow = new HBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
container.AddChild(headerRow);
|
||||
|
||||
_typeDropdown = new OptionButton
|
||||
{
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
CustomMinimumSize = new Vector2(80, 0),
|
||||
};
|
||||
|
||||
foreach (StatescriptVariableType variableType in StatescriptVariableTypeConverter.GetAllTypes())
|
||||
{
|
||||
AddTypeOption(
|
||||
_typeDropdown,
|
||||
variableType,
|
||||
StatescriptVariableTypeConverter.GetDisplayName(variableType));
|
||||
}
|
||||
|
||||
_typeDropdown.Selected = FindSelectedTypeIndex(_typeDropdown);
|
||||
_typeDropdown.ItemSelected += OnTypeDropdownItemSelected;
|
||||
headerRow.AddChild(_typeDropdown);
|
||||
UpdateTypeFoldableTitle();
|
||||
}
|
||||
|
||||
private int FindSelectedTypeIndex(OptionButton dropdown)
|
||||
{
|
||||
for (int i = 0; i < dropdown.ItemCount; i++)
|
||||
{
|
||||
if (dropdown.GetItemId(i) == (int)_selectedType)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void OnTypeDropdownItemSelected(long index)
|
||||
{
|
||||
if (_typeDropdown is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedValue = (StatescriptVariableType)_typeDropdown.GetItemId((int)index);
|
||||
NodeResource.CustomData[ValueTypeKey] = Variant.From((int)selectedValue);
|
||||
_selectedType = selectedValue;
|
||||
UpdateTypeFoldableTitle();
|
||||
|
||||
RemoveBinding(StatescriptPropertyDirection.Input, 0);
|
||||
ActiveResolverEditors.Remove(new PropertySlotKey(StatescriptPropertyDirection.Input, 0));
|
||||
|
||||
if (_cachedTypeInfo is not null
|
||||
&& _inputEditorContainer is not null
|
||||
&& _cachedTypeInfo.InputPropertiesInfo.Length > 0)
|
||||
{
|
||||
RebuildInputEditor(_cachedTypeInfo.InputPropertiesInfo[0]);
|
||||
}
|
||||
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void OnTypeFoldableFoldingChanged(bool folded)
|
||||
{
|
||||
SetFoldStateWithUndo(TypeFoldKey, folded);
|
||||
UpdateTypeFoldableTitle();
|
||||
RefreshInputPropertyFoldableTitles();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void UpdateTypeFoldableTitle()
|
||||
{
|
||||
if (_typeFoldable is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle(
|
||||
"Type:",
|
||||
_typeFoldable,
|
||||
StatescriptVariableTypeConverter.GetDisplayName(_selectedType),
|
||||
InlineSummaryBadgeKind.Enum);
|
||||
}
|
||||
|
||||
private void RebuildInputEditor(StatescriptNodeDiscovery.InputPropertyInfo originalInfo)
|
||||
{
|
||||
if (_inputRootContainer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearValueRows();
|
||||
Type clrType = StatescriptVariableTypeConverter.ToSystemType(_selectedType);
|
||||
_inputEditorContainer = _inputRootContainer;
|
||||
AddInputPropertyRow(
|
||||
new StatescriptNodeDiscovery.InputPropertyInfo(originalInfo.Label, clrType),
|
||||
0,
|
||||
_inputEditorContainer);
|
||||
}
|
||||
|
||||
private void ClearValueRows()
|
||||
{
|
||||
if (_inputRootContainer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Node child in _inputRootContainer.GetChildren())
|
||||
{
|
||||
if (child == _typeFoldable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_inputRootContainer.RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://pr5y56nebti
|
||||
@@ -21,6 +21,8 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
{
|
||||
private const string FoldInputKey = "_fold_input";
|
||||
private const string FoldOutputKey = "_fold_output";
|
||||
private const string ScopeFoldKey = "_fold_output_scope";
|
||||
private const string TargetFoldKey = "_fold_output_target";
|
||||
private const string ScopeKey = "_output_scope";
|
||||
|
||||
private readonly List<string> _setPaths = [];
|
||||
@@ -33,6 +35,8 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
private VBoxContainer? _cachedInputEditorContainer;
|
||||
private VBoxContainer? _cachedTargetContainer;
|
||||
private int _cachedOutputIndex;
|
||||
private FoldableContainer? _scopeFoldable;
|
||||
private FoldableContainer? _targetFoldable;
|
||||
|
||||
private bool _isSharedScope;
|
||||
|
||||
@@ -50,7 +54,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
{
|
||||
_cachedTypeInfo = typeInfo;
|
||||
|
||||
var inputFolded = GetFoldState(FoldInputKey);
|
||||
bool inputFolded = GetFoldState(FoldInputKey);
|
||||
FoldableContainer inputContainer = AddPropertySectionDivider(
|
||||
"Input Properties",
|
||||
InputPropertyColor,
|
||||
@@ -66,7 +70,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
|
||||
inputContainer.AddChild(inputEditorContainer);
|
||||
|
||||
var outputFolded = GetFoldState(FoldOutputKey);
|
||||
bool outputFolded = GetFoldState(FoldOutputKey);
|
||||
FoldableContainer outputContainer = AddPropertySectionDivider(
|
||||
"Output Variables",
|
||||
OutputVariableColor,
|
||||
@@ -122,9 +126,9 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
|
||||
private static void ScanFilesystemDirectory(EditorFileSystemDirectory dir, List<string> results)
|
||||
{
|
||||
for (var i = 0; i < dir.GetFileCount(); i++)
|
||||
for (int i = 0; i < dir.GetFileCount(); i++)
|
||||
{
|
||||
var path = dir.GetFilePath(i);
|
||||
string path = dir.GetFilePath(i);
|
||||
|
||||
if (!path.EndsWith(".tres", StringComparison.InvariantCultureIgnoreCase)
|
||||
&& !path.EndsWith(".res", StringComparison.InvariantCultureIgnoreCase))
|
||||
@@ -140,7 +144,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < dir.GetSubdirCount(); i++)
|
||||
for (int i = 0; i < dir.GetSubdirCount(); i++)
|
||||
{
|
||||
ScanFilesystemDirectory(dir.GetSubdir(i), results);
|
||||
}
|
||||
@@ -190,22 +194,22 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
|
||||
sectionContainer.AddChild(outerVBox);
|
||||
|
||||
_scopeFoldable = new FoldableContainer
|
||||
{
|
||||
Title = "Scope:",
|
||||
Folded = GetFoldState(ScopeFoldKey, true),
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
};
|
||||
_scopeFoldable.FoldingChanged += OnScopeFoldableFoldingChanged;
|
||||
outerVBox.AddChild(_scopeFoldable);
|
||||
|
||||
// Scope toggle row.
|
||||
var scopeRow = new HBoxContainer
|
||||
{
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
};
|
||||
|
||||
outerVBox.AddChild(scopeRow);
|
||||
|
||||
var scopeLabel = new Label
|
||||
{
|
||||
Text = "Scope",
|
||||
CustomMinimumSize = new Vector2(60, 0),
|
||||
};
|
||||
|
||||
scopeLabel.AddThemeColorOverride("font_color", OutputVariableColor);
|
||||
scopeRow.AddChild(scopeLabel);
|
||||
_scopeFoldable.AddChild(scopeRow);
|
||||
|
||||
var graphButton = new CheckBox
|
||||
{
|
||||
@@ -228,6 +232,15 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
scopeRow.AddChild(graphButton);
|
||||
scopeRow.AddChild(sharedButton);
|
||||
|
||||
_targetFoldable = new FoldableContainer
|
||||
{
|
||||
Title = $"{varInfo.Label}:",
|
||||
Folded = GetFoldState(TargetFoldKey, true),
|
||||
SizeFlagsHorizontal = Control.SizeFlags.ExpandFill,
|
||||
};
|
||||
_targetFoldable.FoldingChanged += OnTargetFoldableFoldingChanged;
|
||||
outerVBox.AddChild(_targetFoldable);
|
||||
|
||||
// Target variable container (rebuilt when scope changes).
|
||||
var targetContainer = new VBoxContainer
|
||||
{
|
||||
@@ -235,9 +248,11 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
};
|
||||
|
||||
_cachedTargetContainer = targetContainer;
|
||||
outerVBox.AddChild(targetContainer);
|
||||
_targetFoldable.AddChild(targetContainer);
|
||||
|
||||
RebuildTargetUI(varInfo, index, targetContainer);
|
||||
UpdateScopeFoldableTitle();
|
||||
UpdateTargetFoldableTitle(varInfo.Label);
|
||||
|
||||
graphButton.Pressed += () => OnScopeChanged(false, varInfo, index);
|
||||
sharedButton.Pressed += () => OnScopeChanged(true, varInfo, index);
|
||||
@@ -259,7 +274,9 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
as StatescriptResolverResource;
|
||||
|
||||
_isSharedScope = isShared;
|
||||
NodeResource.CustomData[ScopeKey] = Variant.From(isShared ? (int)VariableScope.Shared : (int)VariableScope.Graph);
|
||||
NodeResource.CustomData[ScopeKey] = Variant.From(isShared
|
||||
? (int)VariableScope.Shared
|
||||
: (int)VariableScope.Graph);
|
||||
|
||||
// Clear the output binding since scope changed.
|
||||
RemoveBinding(StatescriptPropertyDirection.Output, index);
|
||||
@@ -310,6 +327,8 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
newInputResolver,
|
||||
"Change Variable Scope Input");
|
||||
|
||||
UpdateScopeFoldableTitle();
|
||||
UpdateTargetFoldableTitle(varInfo.Label);
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
@@ -365,12 +384,12 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? binding = FindBinding(StatescriptPropertyDirection.Output, index);
|
||||
var selectedIndex = 0;
|
||||
int selectedIndex = 0;
|
||||
|
||||
if (binding?.Resolver is VariableResolverResource varRes
|
||||
&& !string.IsNullOrEmpty(varRes.VariableName))
|
||||
{
|
||||
for (var i = 0; i < Graph.Variables.Count; i++)
|
||||
for (int i = 0; i < Graph.Variables.Count; i++)
|
||||
{
|
||||
if (Graph.Variables[i].VariableName == varRes.VariableName)
|
||||
{
|
||||
@@ -432,6 +451,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
});
|
||||
|
||||
_sharedVarDropdown = new OptionButton { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill };
|
||||
_sharedVarDropdown.SetMeta("is_shared_variable_dropdown", true);
|
||||
PopulateSharedVariableDropdown();
|
||||
varRow.AddChild(_sharedVarDropdown);
|
||||
|
||||
@@ -452,9 +472,9 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
_setDropdown.AddItem("(None)");
|
||||
_setPaths.Add(string.Empty);
|
||||
|
||||
foreach (var path in FindAllSharedVariableSetPaths())
|
||||
foreach (string path in FindAllSharedVariableSetPaths())
|
||||
{
|
||||
var displayName = path[(path.LastIndexOf('/') + 1)..];
|
||||
string displayName = path[(path.LastIndexOf('/') + 1)..];
|
||||
|
||||
if (displayName.EndsWith(".tres", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -466,7 +486,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
}
|
||||
|
||||
// Restore selection.
|
||||
for (var i = 0; i < _setPaths.Count; i++)
|
||||
for (int i = 0; i < _setPaths.Count; i++)
|
||||
{
|
||||
if (_setPaths[i] == _selectedSetPath)
|
||||
{
|
||||
@@ -486,6 +506,8 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
return;
|
||||
}
|
||||
|
||||
_sharedVarDropdown.SetMeta("shared_variable_set_path", _selectedSetPath);
|
||||
|
||||
_sharedVarDropdown.Clear();
|
||||
_variableNames.Clear();
|
||||
|
||||
@@ -498,7 +520,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
|
||||
if (set is not null)
|
||||
{
|
||||
foreach (var variableName in set.Variables.Select(x => x.VariableName))
|
||||
foreach (string? variableName in set.Variables.Select(x => x.VariableName))
|
||||
{
|
||||
if (string.IsNullOrEmpty(variableName))
|
||||
{
|
||||
@@ -512,7 +534,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
}
|
||||
|
||||
// Restore selection.
|
||||
for (var i = 0; i < _variableNames.Count; i++)
|
||||
for (int i = 0; i < _variableNames.Count; i++)
|
||||
{
|
||||
if (_variableNames[i] == _selectedSharedVarName)
|
||||
{
|
||||
@@ -532,7 +554,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = _setDropdown.Selected;
|
||||
int idx = _setDropdown.Selected;
|
||||
|
||||
var oldResolver = FindBinding(StatescriptPropertyDirection.Output, _cachedOutputIndex)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
@@ -597,7 +619,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = _sharedVarDropdown.Selected;
|
||||
int idx = _sharedVarDropdown.Selected;
|
||||
|
||||
var oldResolver = FindBinding(StatescriptPropertyDirection.Output, _cachedOutputIndex)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
@@ -605,7 +627,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
as StatescriptResolverResource;
|
||||
|
||||
StatescriptVariableType? previousType = _resolvedType;
|
||||
var previousIsArray = _resolvedIsArray;
|
||||
bool previousIsArray = _resolvedIsArray;
|
||||
|
||||
if (idx >= 0 && idx < _variableNames.Count)
|
||||
{
|
||||
@@ -666,6 +688,11 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
"Change Shared Target Variable Input");
|
||||
}
|
||||
|
||||
if (_cachedTypeInfo?.OutputVariablesInfo.Length > 0)
|
||||
{
|
||||
UpdateTargetFoldableTitle(_cachedTypeInfo.OutputVariablesInfo[0].Label);
|
||||
}
|
||||
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
@@ -724,11 +751,11 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
return;
|
||||
}
|
||||
|
||||
var index = _cachedOutputIndex;
|
||||
var variableIndex = (int)x - 1;
|
||||
int index = _cachedOutputIndex;
|
||||
int variableIndex = (int)x - 1;
|
||||
|
||||
StatescriptVariableType? previousType = _resolvedType;
|
||||
var previousIsArray = _resolvedIsArray;
|
||||
bool previousIsArray = _resolvedIsArray;
|
||||
|
||||
var oldOutputResolver = FindBinding(StatescriptPropertyDirection.Output, index)?.Resolver?.Duplicate()
|
||||
as StatescriptResolverResource;
|
||||
@@ -743,7 +770,7 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
var variableName = Graph.Variables[variableIndex].VariableName;
|
||||
string variableName = Graph.Variables[variableIndex].VariableName;
|
||||
EnsureBinding(StatescriptPropertyDirection.Output, index).Resolver =
|
||||
new VariableResolverResource { VariableName = variableName };
|
||||
|
||||
@@ -787,10 +814,97 @@ internal sealed partial class SetVariableNodeEditor : CustomNodeEditor
|
||||
"Change Target Variable Input");
|
||||
}
|
||||
|
||||
if (_cachedTypeInfo?.OutputVariablesInfo.Length > 0)
|
||||
{
|
||||
UpdateTargetFoldableTitle(_cachedTypeInfo.OutputVariablesInfo[0].Label);
|
||||
}
|
||||
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void OnScopeFoldableFoldingChanged(bool folded)
|
||||
{
|
||||
SetFoldStateWithUndo(ScopeFoldKey, folded);
|
||||
UpdateScopeFoldableTitle();
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void OnTargetFoldableFoldingChanged(bool folded)
|
||||
{
|
||||
SetFoldStateWithUndo(TargetFoldKey, folded);
|
||||
|
||||
if (_cachedTypeInfo?.OutputVariablesInfo.Length > 0)
|
||||
{
|
||||
UpdateTargetFoldableTitle(_cachedTypeInfo.OutputVariablesInfo[0].Label);
|
||||
}
|
||||
|
||||
RaisePropertyBindingChanged();
|
||||
ResetSize();
|
||||
}
|
||||
|
||||
private void UpdateScopeFoldableTitle()
|
||||
{
|
||||
if (_scopeFoldable is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle(
|
||||
"Scope:",
|
||||
_scopeFoldable,
|
||||
_isSharedScope ? "Shared" : "Graph",
|
||||
InlineSummaryBadgeKind.Enum);
|
||||
}
|
||||
|
||||
private void UpdateTargetFoldableTitle(string label)
|
||||
{
|
||||
if (_targetFoldable is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string summary = _isSharedScope
|
||||
? _selectedSharedVarName
|
||||
: GetSelectedGraphVariableName();
|
||||
|
||||
InlineSummaryBadgeKind badgeKind = _isSharedScope
|
||||
? InlineSummaryBadgeKind.SharedVariable
|
||||
: InlineSummaryBadgeKind.Variable;
|
||||
|
||||
string highlightedVariableName = !_isSharedScope
|
||||
? summary
|
||||
: string.Empty;
|
||||
string highlightedSharedVariableSetPath = _isSharedScope ? _selectedSetPath : string.Empty;
|
||||
string highlightedSharedVariableName = _isSharedScope ? summary : string.Empty;
|
||||
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle(
|
||||
$"{label}:",
|
||||
_targetFoldable,
|
||||
string.IsNullOrWhiteSpace(summary) ? "(None)" : summary,
|
||||
badgeKind,
|
||||
highlightedVariableName: highlightedVariableName,
|
||||
highlightedSharedVariableSetPath: highlightedSharedVariableSetPath,
|
||||
highlightedSharedVariableName: highlightedSharedVariableName);
|
||||
}
|
||||
|
||||
private string GetSelectedGraphVariableName()
|
||||
{
|
||||
if (_cachedOutputIndex < 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (FindBinding(StatescriptPropertyDirection.Output, _cachedOutputIndex)?.Resolver
|
||||
is VariableResolverResource varRes)
|
||||
{
|
||||
return varRes.VariableName;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void RebuildInputUI(
|
||||
StatescriptNodeDiscovery.InputPropertyInfo propInfo,
|
||||
VBoxContainer container)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ACosHResolverEditor : ScalarUnaryResolverEditorBase<ACosHResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ACosH";
|
||||
|
||||
public override string ResolverTypeId => "ACosH";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://b71ewdriai7i8
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ACosResolverEditor : ScalarUnaryResolverEditorBase<ACosResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ACos";
|
||||
|
||||
public override string ResolverTypeId => "ACos";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://bpk2fmsjil46f
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ASinHResolverEditor : ScalarUnaryResolverEditorBase<ASinHResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ASinH";
|
||||
|
||||
public override string ResolverTypeId => "ASinH";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://bifsuao1kt1do
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ASinResolverEditor : ScalarUnaryResolverEditorBase<ASinResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ASin";
|
||||
|
||||
public override string ResolverTypeId => "ASin";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0jphwgil1ufa
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ATan2ResolverEditor : ScalarBinaryResolverEditorBase<ATan2ResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ATan2";
|
||||
|
||||
public override string ResolverTypeId => "ATan2";
|
||||
|
||||
protected override string LeftTitle => "Y:";
|
||||
|
||||
protected override string RightTitle => "X:";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://ev7sos0vifou
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ATanHResolverEditor : ScalarUnaryResolverEditorBase<ATanHResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ATanH";
|
||||
|
||||
public override string ResolverTypeId => "ATanH";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://j14187spggme
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ATanResolverEditor : ScalarUnaryResolverEditorBase<ATanResolverResource>
|
||||
{
|
||||
public override string DisplayName => "ATan";
|
||||
|
||||
public override string ResolverTypeId => "ATan";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0owkewrrka0c
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class AbsResolverEditor : NumericOrVectorUnaryResolverEditorBase<AbsResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Abs";
|
||||
|
||||
public override string ResolverTypeId => "Abs";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128)
|
||||
? [typeof(int), typeof(float), typeof(double), typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)]
|
||||
: [expectedType];
|
||||
}
|
||||
|
||||
protected override Type GetNestedExpectedType(Type expectedType)
|
||||
{
|
||||
return expectedType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dyytfhlb0wts
|
||||
@@ -129,6 +129,11 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
_onChanged = null;
|
||||
}
|
||||
|
||||
private static bool IsCompatibleType(Type expectedType, StatescriptVariableType fieldType)
|
||||
{
|
||||
return StatescriptVariableTypeConverter.IsCompatible(expectedType, fieldType);
|
||||
}
|
||||
|
||||
private static string FindExistingProvider(StatescriptGraph graph, StatescriptNodeProperty? currentProperty)
|
||||
{
|
||||
foreach (StatescriptNode node in graph.Nodes)
|
||||
@@ -158,13 +163,7 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
return null;
|
||||
}
|
||||
|
||||
Type? type = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.FirstOrDefault(
|
||||
x => typeof(IActivationDataProvider).IsAssignableFrom(x)
|
||||
&& !x.IsAbstract
|
||||
&& !x.IsInterface
|
||||
&& x.Name == className);
|
||||
Type? type = ResolveProviderType(className);
|
||||
|
||||
if (type is null)
|
||||
{
|
||||
@@ -174,6 +173,43 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
return Activator.CreateInstance(type) as IActivationDataProvider;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetProviderTypes()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(x => typeof(IActivationDataProvider).IsAssignableFrom(x)
|
||||
&& !x.IsAbstract
|
||||
&& !x.IsInterface);
|
||||
}
|
||||
|
||||
private static string GetProviderIdentifier(Type type)
|
||||
{
|
||||
return type.FullName ?? type.Name;
|
||||
}
|
||||
|
||||
private static bool ProviderMatches(Type type, string providerIdentifier)
|
||||
{
|
||||
return type.AssemblyQualifiedName == providerIdentifier
|
||||
|| type.FullName == providerIdentifier
|
||||
|| type.Name == providerIdentifier;
|
||||
}
|
||||
|
||||
private static Type? ResolveProviderType(string providerIdentifier)
|
||||
{
|
||||
return GetProviderTypes().FirstOrDefault(x => ProviderMatches(x, providerIdentifier));
|
||||
}
|
||||
|
||||
private static bool ProviderIdentifiersMatch(string left, string right)
|
||||
{
|
||||
if (left == right)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Type? leftType = ResolveProviderType(left);
|
||||
return leftType is not null && ProviderMatches(leftType, right);
|
||||
}
|
||||
|
||||
private void OnProviderDropdownItemSelected(long index)
|
||||
{
|
||||
if (_providerDropdown is null)
|
||||
@@ -181,7 +217,7 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = _providerDropdown.Selected;
|
||||
int idx = _providerDropdown.Selected;
|
||||
_selectedProviderClassName = idx >= 0 && idx < _providerClassNames.Count
|
||||
? _providerClassNames[idx]
|
||||
: string.Empty;
|
||||
@@ -200,7 +236,7 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
return;
|
||||
}
|
||||
|
||||
var dropdownIndex = _fieldDropdown.Selected;
|
||||
int dropdownIndex = _fieldDropdown.Selected;
|
||||
|
||||
if (dropdownIndex >= 0 && dropdownIndex < _fieldNames.Count)
|
||||
{
|
||||
@@ -239,37 +275,39 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
_providerClassNames.Add(string.Empty);
|
||||
|
||||
// Re-scan the graph each time to pick up changes from other editors.
|
||||
var graphLockedProvider = _graph is not null
|
||||
string graphLockedProvider = _graph is not null
|
||||
? FindExistingProvider(_graph, _currentProperty)
|
||||
: string.Empty;
|
||||
|
||||
if (!string.IsNullOrEmpty(graphLockedProvider))
|
||||
{
|
||||
// Another node already uses a provider: only show that one (plus None).
|
||||
_providerDropdown.AddItem(graphLockedProvider);
|
||||
_providerClassNames.Add(graphLockedProvider);
|
||||
Type? lockedProviderType = ResolveProviderType(graphLockedProvider);
|
||||
string lockedProviderIdentifier = lockedProviderType is null
|
||||
? graphLockedProvider
|
||||
: GetProviderIdentifier(lockedProviderType);
|
||||
string lockedProviderDisplayName = lockedProviderType?.Name ?? graphLockedProvider;
|
||||
|
||||
_providerDropdown.AddItem(lockedProviderDisplayName);
|
||||
_providerClassNames.Add(lockedProviderIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var name in AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(x => typeof(IActivationDataProvider).IsAssignableFrom(x)
|
||||
&& !x.IsAbstract
|
||||
&& !x.IsInterface)
|
||||
.Select(x => x.Name))
|
||||
foreach (Type providerType in GetProviderTypes())
|
||||
{
|
||||
_providerDropdown.AddItem(name);
|
||||
_providerClassNames.Add(name);
|
||||
_providerDropdown.AddItem(providerType.Name);
|
||||
_providerClassNames.Add(GetProviderIdentifier(providerType));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore selection.
|
||||
if (!string.IsNullOrEmpty(_selectedProviderClassName))
|
||||
{
|
||||
for (var i = 0; i < _providerClassNames.Count; i++)
|
||||
for (int i = 0; i < _providerClassNames.Count; i++)
|
||||
{
|
||||
if (_providerClassNames[i] == _selectedProviderClassName)
|
||||
if (ProviderIdentifiersMatch(_providerClassNames[i], _selectedProviderClassName))
|
||||
{
|
||||
_selectedProviderClassName = _providerClassNames[i];
|
||||
_providerDropdown.Selected = i;
|
||||
return;
|
||||
}
|
||||
@@ -306,8 +344,7 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_expectedType != typeof(Variant128)
|
||||
&& !StatescriptVariableTypeConverter.IsCompatible(_expectedType, field.FieldType))
|
||||
if (!IsCompatibleType(_expectedType, field.FieldType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -320,7 +357,7 @@ internal sealed partial class ActivationDataResolverEditor : NodeEditorProperty
|
||||
// Restore selection.
|
||||
if (!string.IsNullOrEmpty(_selectedFieldName))
|
||||
{
|
||||
for (var i = 0; i < _fieldNames.Count; i++)
|
||||
for (int i = 0; i < _fieldNames.Count; i++)
|
||||
{
|
||||
if (_fieldNames[i] == _selectedFieldName)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class AddResolverEditor : NumericVectorOrQuaternionBinaryResolverEditorBase<AddResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Add";
|
||||
|
||||
public override string ResolverTypeId => "Add";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128)
|
||||
? [
|
||||
typeof(int),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(SysVector2),
|
||||
typeof(SysVector3),
|
||||
typeof(SysVector4),
|
||||
typeof(System.Numerics.Quaternion)
|
||||
]
|
||||
: [expectedType];
|
||||
}
|
||||
|
||||
protected override Type GetNestedExpectedType(Type expectedType)
|
||||
{
|
||||
return expectedType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://crmxx52lmd0nm
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class AndResolverEditor : BooleanBinaryResolverEditorBase<AndResolverResource>
|
||||
{
|
||||
public override string DisplayName => "And";
|
||||
|
||||
public override string ResolverTypeId => "And";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://cms31o1pqity
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class AngleResolverEditor
|
||||
: VectorOrQuaternionBinaryFloatResolverEditorBase<AngleResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Angle";
|
||||
|
||||
public override string ResolverTypeId => "Angle";
|
||||
|
||||
protected override string LeftTitle => "From:";
|
||||
|
||||
protected override string RightTitle => "To:";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
if (expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsFloatType(expectedType))
|
||||
{
|
||||
return [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(System.Numerics.Quaternion)];
|
||||
}
|
||||
|
||||
return [expectedType];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmgy6p45g6p8p
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ArrayVariableResolverEditor : NodeEditorProperty
|
||||
{
|
||||
private VariantResolverEditor? _innerEditor;
|
||||
|
||||
public override string DisplayName => "Array";
|
||||
|
||||
public override string ResolverTypeId => "ArrayVariable";
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType.IsArray;
|
||||
}
|
||||
|
||||
public override void Setup(
|
||||
StatescriptGraph graph,
|
||||
StatescriptNodeProperty? property,
|
||||
Type expectedType,
|
||||
Action onChanged,
|
||||
bool isArray)
|
||||
{
|
||||
_innerEditor = new VariantResolverEditor();
|
||||
Type elementExpectedType = expectedType.IsArray ? expectedType.GetElementType() ?? typeof(int) : expectedType;
|
||||
VariantResolverResource? tempResolver = null;
|
||||
|
||||
if (property?.Resolver is ArrayVariableResolverResource existing)
|
||||
{
|
||||
tempResolver = new VariantResolverResource
|
||||
{
|
||||
IsArray = true,
|
||||
ValueType = existing.ValueType,
|
||||
ArrayValues = [.. existing.ArrayValues],
|
||||
IsArrayExpanded = existing.IsArrayExpanded,
|
||||
};
|
||||
}
|
||||
|
||||
StatescriptNodeProperty? tempProperty = tempResolver is null
|
||||
? null
|
||||
: new StatescriptNodeProperty { Resolver = tempResolver };
|
||||
|
||||
_innerEditor.Setup(graph, tempProperty, elementExpectedType, onChanged, true);
|
||||
_innerEditor.LayoutSizeChanged += RaiseLayoutSizeChanged;
|
||||
AddChild(_innerEditor);
|
||||
}
|
||||
|
||||
public override void SaveTo(StatescriptNodeProperty property)
|
||||
{
|
||||
if (_innerEditor is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tempProperty = new StatescriptNodeProperty();
|
||||
_innerEditor.SaveTo(tempProperty);
|
||||
|
||||
if (tempProperty.Resolver is not VariantResolverResource variant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
property.Resolver = new ArrayVariableResolverResource
|
||||
{
|
||||
ValueType = variant.ValueType,
|
||||
ArrayValues = [.. variant.ArrayValues],
|
||||
IsArrayExpanded = variant.IsArrayExpanded,
|
||||
};
|
||||
}
|
||||
|
||||
public override void ClearCallbacks()
|
||||
{
|
||||
base.ClearCallbacks();
|
||||
_innerEditor?.ClearCallbacks();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://30cl3jyw0pvd
|
||||
@@ -135,7 +135,7 @@ internal sealed partial class AttributeResolverEditor : NodeEditorProperty
|
||||
|
||||
_setDropdown.Clear();
|
||||
|
||||
foreach (var option in EditorUtils.GetAttributeSetOptions())
|
||||
foreach (string option in EditorUtils.GetAttributeSetOptions())
|
||||
{
|
||||
_setDropdown.AddItem(option);
|
||||
}
|
||||
@@ -143,7 +143,7 @@ internal sealed partial class AttributeResolverEditor : NodeEditorProperty
|
||||
// Restore selection.
|
||||
if (!string.IsNullOrEmpty(_selectedSetClass))
|
||||
{
|
||||
for (var i = 0; i < _setDropdown.GetItemCount(); i++)
|
||||
for (int i = 0; i < _setDropdown.GetItemCount(); i++)
|
||||
{
|
||||
if (_setDropdown.GetItemText(i) == _selectedSetClass)
|
||||
{
|
||||
@@ -170,14 +170,14 @@ internal sealed partial class AttributeResolverEditor : NodeEditorProperty
|
||||
|
||||
_attributeDropdown.Clear();
|
||||
|
||||
foreach (var option in EditorUtils.GetAttributeOptions(_selectedSetClass))
|
||||
foreach (string option in EditorUtils.GetAttributeOptions(_selectedSetClass))
|
||||
{
|
||||
_attributeDropdown.AddItem(option);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_selectedAttribute))
|
||||
{
|
||||
for (var i = 0; i < _attributeDropdown.GetItemCount(); i++)
|
||||
for (int i = 0; i < _attributeDropdown.GetItemCount(); i++)
|
||||
{
|
||||
if (_attributeDropdown.GetItemText(i) == _selectedAttribute)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CbrtResolverEditor : ScalarUnaryResolverEditorBase<CbrtResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Cbrt";
|
||||
|
||||
public override string ResolverTypeId => "Cbrt";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://s0f2lnjo4li4
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CeilResolverEditor : FloatUnaryResolverEditorBase<CeilResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Ceil";
|
||||
|
||||
public override string ResolverTypeId => "Ceil";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dth47pdwwobn8
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ClampMagnitudeResolverEditor
|
||||
: AsymmetricBinaryNestedResolverEditorBase<ClampMagnitudeResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Clamp Magnitude";
|
||||
|
||||
public override string ResolverTypeId => "ClampMagnitude";
|
||||
|
||||
protected override Type[] LeftFactoryExpectedTypes => [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)];
|
||||
|
||||
protected override Type[] RightFactoryExpectedTypes => ResolverEditorCompatibility.FloatOperandExpectedTypes;
|
||||
|
||||
protected override Type LeftNestedExpectedType => typeof(ForgeVariant128);
|
||||
|
||||
protected override Type RightNestedExpectedType => typeof(float);
|
||||
|
||||
protected override string LeftTitle => "Value:";
|
||||
|
||||
protected override string RightTitle => "Max Length:";
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsVectorType(expectedType);
|
||||
}
|
||||
|
||||
protected override Type[] GetLeftFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128)
|
||||
? [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)]
|
||||
: [expectedType];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dgp53mpcxxe8e
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ClampResolverEditor : NumericOrVectorTernaryResolverEditorBase<ClampResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Clamp";
|
||||
|
||||
public override string ResolverTypeId => "Clamp";
|
||||
|
||||
protected override string FirstTitle => "Value:";
|
||||
|
||||
protected override string SecondTitle => "Min:";
|
||||
|
||||
protected override string ThirdTitle => "Max:";
|
||||
|
||||
protected override Type[] GetFirstFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128)
|
||||
? [typeof(int), typeof(float), typeof(double), typeof(SysVector2), typeof(SysVector3), typeof(SysVector4)]
|
||||
: [expectedType];
|
||||
}
|
||||
|
||||
protected override Type[] GetSecondFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return GetFirstFactoryExpectedTypes(expectedType);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://caqnwfy35n5i
|
||||
@@ -3,6 +3,7 @@
|
||||
#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 Gamesmiths.Forge.Statescript.Properties;
|
||||
@@ -18,12 +19,16 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
[Tool]
|
||||
internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
{
|
||||
private static readonly Type[] _numericExpectedTypes = [typeof(int), typeof(float), typeof(double)];
|
||||
|
||||
private StatescriptGraph? _graph;
|
||||
private Action? _onChanged;
|
||||
|
||||
private OptionButton? _operationDropdown;
|
||||
private VBoxContainer? _leftContainer;
|
||||
private VBoxContainer? _rightContainer;
|
||||
private FoldableContainer? _leftFoldable;
|
||||
private FoldableContainer? _rightFoldable;
|
||||
private OptionButton? _leftResolverDropdown;
|
||||
private OptionButton? _rightResolverDropdown;
|
||||
|
||||
@@ -63,7 +68,7 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
AddChild(vBox);
|
||||
|
||||
_numericFactories = StatescriptResolverRegistry.GetCompatibleFactories(typeof(ForgeVariant128));
|
||||
_numericFactories = ResolverEditorFactoryCatalog.GetCompatibleFactories(_numericExpectedTypes);
|
||||
|
||||
_numericFactories.RemoveAll(x =>
|
||||
{
|
||||
@@ -78,12 +83,17 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
_operation = comparisonResolver.Operation;
|
||||
}
|
||||
|
||||
var leftFoldable = new FoldableContainer { Title = "Left:" };
|
||||
leftFoldable.FoldingChanged += OnFoldingChanged;
|
||||
vBox.AddChild(leftFoldable);
|
||||
_leftFoldable = new FoldableContainer
|
||||
{
|
||||
Title = "Left:",
|
||||
Folded = comparisonResolver?.LeftFolded ?? true,
|
||||
};
|
||||
|
||||
_leftFoldable.FoldingChanged += OnFoldingChanged;
|
||||
vBox.AddChild(_leftFoldable);
|
||||
|
||||
_leftContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
leftFoldable.AddChild(_leftContainer);
|
||||
_leftFoldable.AddChild(_leftContainer);
|
||||
|
||||
_leftEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_leftResolverDropdown = CreateResolverDropdownControl(comparisonResolver?.Left);
|
||||
@@ -117,12 +127,16 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
|
||||
opRow.AddChild(_operationDropdown);
|
||||
|
||||
var rightFoldable = new FoldableContainer { Title = "Right:" };
|
||||
rightFoldable.FoldingChanged += OnFoldingChanged;
|
||||
vBox.AddChild(rightFoldable);
|
||||
_rightFoldable = new FoldableContainer
|
||||
{
|
||||
Title = "Right:",
|
||||
Folded = comparisonResolver?.RightFolded ?? true,
|
||||
};
|
||||
_rightFoldable.FoldingChanged += OnFoldingChanged;
|
||||
vBox.AddChild(_rightFoldable);
|
||||
|
||||
_rightContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
rightFoldable.AddChild(_rightContainer);
|
||||
_rightFoldable.AddChild(_rightContainer);
|
||||
|
||||
_rightEditorContainer = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_rightResolverDropdown = CreateResolverDropdownControl(comparisonResolver?.Right);
|
||||
@@ -135,12 +149,18 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
x => _rightEditor = x);
|
||||
|
||||
_rightResolverDropdown.ItemSelected += OnRightResolverDropdownItemSelected;
|
||||
UpdateFoldableTitles();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SaveTo(StatescriptNodeProperty property)
|
||||
{
|
||||
var comparisonResolver = new ComparisonResolverResource { Operation = _operation };
|
||||
var comparisonResolver = new ComparisonResolverResource
|
||||
{
|
||||
Operation = _operation,
|
||||
LeftFolded = _leftFoldable?.Folded ?? false,
|
||||
RightFolded = _rightFoldable?.Folded ?? false,
|
||||
};
|
||||
|
||||
if (_leftEditor is not null)
|
||||
{
|
||||
@@ -167,10 +187,23 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
|
||||
_leftEditor?.ClearCallbacks();
|
||||
_rightEditor?.ClearCallbacks();
|
||||
_operationDropdown = null;
|
||||
_leftContainer = null;
|
||||
_rightContainer = null;
|
||||
_leftFoldable = null;
|
||||
_rightFoldable = null;
|
||||
_leftResolverDropdown = null;
|
||||
_rightResolverDropdown = null;
|
||||
_leftEditor = null;
|
||||
_rightEditor = null;
|
||||
_leftEditorContainer = null;
|
||||
_rightEditorContainer = null;
|
||||
}
|
||||
|
||||
private void OnFoldingChanged(bool isFolded)
|
||||
{
|
||||
UpdateFoldableTitles();
|
||||
_onChanged?.Invoke();
|
||||
RaiseLayoutSizeChanged();
|
||||
}
|
||||
|
||||
@@ -208,31 +241,14 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
|
||||
setEditor(null);
|
||||
ShowNestedEditor(selectedIndex, null, editorContainer, setEditor);
|
||||
UpdateFoldableTitles();
|
||||
_onChanged?.Invoke();
|
||||
RaiseLayoutSizeChanged();
|
||||
}
|
||||
|
||||
private int GetSelectedIndex(StatescriptResolverResource? existingResolver)
|
||||
{
|
||||
var selectedIndex = 0;
|
||||
|
||||
if (existingResolver is not null)
|
||||
{
|
||||
var existingTypeId = existingResolver.ResolverTypeId;
|
||||
|
||||
for (var i = 0; i < _numericFactories.Count; i++)
|
||||
{
|
||||
using NodeEditorProperty temp = _numericFactories[i]();
|
||||
|
||||
if (temp.ResolverTypeId == existingTypeId)
|
||||
{
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedIndex;
|
||||
return ResolverEditorFactoryCatalog.GetDefaultFactoryIndex(_numericFactories, existingResolver, "Variant");
|
||||
}
|
||||
|
||||
private OptionButton CreateResolverDropdownControl(StatescriptResolverResource? existingResolver)
|
||||
@@ -241,8 +257,7 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
|
||||
foreach (Func<NodeEditorProperty> factory in _numericFactories)
|
||||
{
|
||||
using NodeEditorProperty temp = factory();
|
||||
dropdown.AddItem(temp.DisplayName);
|
||||
dropdown.AddItem(StatescriptResolverRegistry.GetDisplayName(factory));
|
||||
}
|
||||
|
||||
dropdown.Selected = GetSelectedIndex(existingResolver);
|
||||
@@ -261,26 +276,41 @@ internal sealed partial class ComparisonResolverEditor : NodeEditorProperty
|
||||
return;
|
||||
}
|
||||
|
||||
NodeEditorProperty editor = _numericFactories[factoryIndex]();
|
||||
NodeEditorProperty? editor = NestedResolverEditorUtilities.CreateNestedEditor(
|
||||
_graph,
|
||||
_numericFactories,
|
||||
factoryIndex,
|
||||
existingResolver,
|
||||
_numericExpectedTypes,
|
||||
OnNestedEditorChanged,
|
||||
RaiseLayoutSizeChanged);
|
||||
|
||||
StatescriptNodeProperty? tempProperty = null;
|
||||
|
||||
if (existingResolver is not null)
|
||||
if (editor is null)
|
||||
{
|
||||
tempProperty = new StatescriptNodeProperty { Resolver = existingResolver };
|
||||
return;
|
||||
}
|
||||
|
||||
editor.Setup(_graph, tempProperty, typeof(ForgeVariant128), OnNestedEditorChanged, false);
|
||||
|
||||
editor.LayoutSizeChanged += RaiseLayoutSizeChanged;
|
||||
|
||||
container.AddChild(editor);
|
||||
setEditor(editor);
|
||||
}
|
||||
|
||||
private void OnNestedEditorChanged()
|
||||
{
|
||||
UpdateFoldableTitles();
|
||||
_onChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void UpdateFoldableTitles()
|
||||
{
|
||||
if (_leftFoldable is not null)
|
||||
{
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle("Left:", _leftFoldable, _leftEditor);
|
||||
}
|
||||
|
||||
if (_rightFoldable is not null)
|
||||
{
|
||||
InlineConstantSummaryFormatter.ApplyFoldableTitle("Right:", _rightFoldable, _rightEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
|
||||
using SysQuaternion = System.Numerics.Quaternion;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ConcatenateResolverEditor : BinaryNestedResolverEditorBase<ConcatenateResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Concatenate";
|
||||
|
||||
public override string ResolverTypeId => "Concatenate";
|
||||
|
||||
protected override Type[] FactoryExpectedTypes => [typeof(SysQuaternion)];
|
||||
|
||||
protected override Type NestedExpectedType => typeof(SysQuaternion);
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(SysQuaternion) || expectedType == typeof(ForgeVariant128);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://sigmt6powc5m
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
|
||||
using SysQuaternion = System.Numerics.Quaternion;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class ConjugateResolverEditor : UnaryNestedResolverEditorBase<ConjugateResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Conjugate";
|
||||
|
||||
public override string ResolverTypeId => "Conjugate";
|
||||
|
||||
protected override Type[] FactoryExpectedTypes => [typeof(SysQuaternion)];
|
||||
|
||||
protected override Type NestedExpectedType => typeof(SysQuaternion);
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(SysQuaternion) || expectedType == typeof(ForgeVariant128);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://du4pq8lhfvd6g
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CopySignResolverEditor : ScalarBinaryResolverEditorBase<CopySignResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Copy Sign";
|
||||
|
||||
public override string ResolverTypeId => "CopySign";
|
||||
|
||||
protected override string LeftTitle => "Magnitude:";
|
||||
|
||||
protected override string RightTitle => "Sign:";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://ce3kvc4llldxi
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CosHResolverEditor : ScalarUnaryResolverEditorBase<CosHResolverResource>
|
||||
{
|
||||
public override string DisplayName => "CosH";
|
||||
|
||||
public override string ResolverTypeId => "CosH";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://bf6hoojiyod7b
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CosResolverEditor : ScalarUnaryResolverEditorBase<CosResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Cos";
|
||||
|
||||
public override string ResolverTypeId => "Cos";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://3iou2ih6sonq
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class CrossResolverEditor : VectorBinaryResolverEditorBase<CrossResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Cross";
|
||||
|
||||
public override string ResolverTypeId => "Cross";
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(SysVector3) || expectedType == typeof(ForgeVariant128);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://00ltb2mbsruh
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DegToRadResolverEditor : NumericOrVectorUnaryResolverEditorBase<DegToRadResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Deg To Rad";
|
||||
|
||||
public override string ResolverTypeId => "DegToRad";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://f3spr5g0o33e
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DistanceResolverEditor
|
||||
: VectorOrQuaternionBinaryFloatResolverEditorBase<DistanceResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Distance";
|
||||
|
||||
public override string ResolverTypeId => "Distance";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
if (expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsFloatType(expectedType))
|
||||
{
|
||||
return [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(System.Numerics.Quaternion)];
|
||||
}
|
||||
|
||||
return [expectedType];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dfagvimhepgjm
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DistanceSquaredResolverEditor
|
||||
: VectorOrQuaternionBinaryFloatResolverEditorBase<DistanceSquaredResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Distance Squared";
|
||||
|
||||
public override string ResolverTypeId => "DistanceSquared";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
if (expectedType == typeof(ForgeVariant128) || ResolverEditorCompatibility.IsFloatType(expectedType))
|
||||
{
|
||||
return [typeof(SysVector2), typeof(SysVector3), typeof(SysVector4), typeof(System.Numerics.Quaternion)];
|
||||
}
|
||||
|
||||
return [expectedType];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dq03h0fpsh0b3
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
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;
|
||||
using SysVector4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DivideResolverEditor
|
||||
: NumericVectorOrQuaternionBinaryResolverEditorBase<DivideResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Divide";
|
||||
|
||||
public override string ResolverTypeId => "Divide";
|
||||
|
||||
protected override Type[] GetFactoryExpectedTypes(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(ForgeVariant128)
|
||||
? [
|
||||
typeof(int),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(SysVector2),
|
||||
typeof(SysVector3),
|
||||
typeof(SysVector4),
|
||||
typeof(System.Numerics.Quaternion)
|
||||
]
|
||||
: [expectedType];
|
||||
}
|
||||
|
||||
protected override Type GetNestedExpectedType(Type expectedType)
|
||||
{
|
||||
return expectedType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://2gj0lejcirb6
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
|
||||
using SysPlane = System.Numerics.Plane;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DotCoordinateResolverEditor
|
||||
: AsymmetricBinaryNestedResolverEditorBase<DotCoordinateResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Dot Coordinate";
|
||||
|
||||
public override string ResolverTypeId => "DotCoordinate";
|
||||
|
||||
protected override Type[] LeftFactoryExpectedTypes => [typeof(SysPlane)];
|
||||
|
||||
protected override Type[] RightFactoryExpectedTypes => [typeof(SysVector3)];
|
||||
|
||||
protected override Type LeftNestedExpectedType => typeof(SysPlane);
|
||||
|
||||
protected override Type RightNestedExpectedType => typeof(SysVector3);
|
||||
|
||||
protected override string LeftTitle => "Plane:";
|
||||
|
||||
protected override string RightTitle => "Coordinate:";
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(float) || expectedType == typeof(ForgeVariant128);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://cw0ggwtmgsnem
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright © Gamesmiths Guild.
|
||||
|
||||
#if TOOLS
|
||||
using System;
|
||||
using Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers.Bases;
|
||||
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
|
||||
using Godot;
|
||||
using ForgeVariant128 = Gamesmiths.Forge.Statescript.Variant128;
|
||||
using SysPlane = System.Numerics.Plane;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
|
||||
namespace Gamesmiths.Forge.Godot.Editor.Statescript.Resolvers;
|
||||
|
||||
[Tool]
|
||||
internal sealed partial class DotNormalResolverEditor
|
||||
: AsymmetricBinaryNestedResolverEditorBase<DotNormalResolverResource>
|
||||
{
|
||||
public override string DisplayName => "Dot Normal";
|
||||
|
||||
public override string ResolverTypeId => "DotNormal";
|
||||
|
||||
protected override Type[] LeftFactoryExpectedTypes => [typeof(SysPlane)];
|
||||
|
||||
protected override Type[] RightFactoryExpectedTypes => [typeof(SysVector3)];
|
||||
|
||||
protected override Type LeftNestedExpectedType => typeof(SysPlane);
|
||||
|
||||
protected override Type RightNestedExpectedType => typeof(SysVector3);
|
||||
|
||||
protected override string LeftTitle => "Plane:";
|
||||
|
||||
protected override string RightTitle => "Normal:";
|
||||
|
||||
public override bool IsCompatibleWith(Type expectedType)
|
||||
{
|
||||
return expectedType == typeof(float) || expectedType == typeof(ForgeVariant128);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user