Add scripting API events in Visual Script
This commit is contained in:
@@ -53,6 +53,8 @@ namespace FlaxEditor.Scripting
|
||||
return fieldInfo.IsPublic;
|
||||
if (_managed is PropertyInfo propertyInfo)
|
||||
return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsPublic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsPublic);
|
||||
if (_managed is EventInfo eventInfo)
|
||||
return eventInfo.GetAddMethod().IsPublic;
|
||||
if (_custom != null)
|
||||
return _custom.IsPublic;
|
||||
return false;
|
||||
@@ -72,6 +74,8 @@ namespace FlaxEditor.Scripting
|
||||
return fieldInfo.IsStatic;
|
||||
if (_managed is PropertyInfo propertyInfo)
|
||||
return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsStatic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsStatic);
|
||||
if (_managed is EventInfo eventInfo)
|
||||
return eventInfo.GetAddMethod().IsStatic;
|
||||
if (_custom != null)
|
||||
return _custom.IsStatic;
|
||||
return false;
|
||||
@@ -178,7 +182,7 @@ namespace FlaxEditor.Scripting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method parameters count (valid for methods only).
|
||||
/// Gets the method parameters count (valid for methods and events only).
|
||||
/// </summary>
|
||||
public int ParametersCount
|
||||
{
|
||||
@@ -186,6 +190,8 @@ namespace FlaxEditor.Scripting
|
||||
{
|
||||
if (_managed is MethodInfo methodInfo)
|
||||
return methodInfo.GetParameters().Length;
|
||||
if (_managed is EventInfo eventInfo)
|
||||
return eventInfo.EventHandlerType.GetMethod("Invoke").GetParameters().Length;
|
||||
if (_custom != null)
|
||||
return _custom.ParametersCount;
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -1180,6 +1181,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[TypeReference(typeof(object), nameof(IsTypeValid))]
|
||||
public ScriptType Type;
|
||||
|
||||
public Parameter(ref ScriptMemberInfo.Parameter param)
|
||||
{
|
||||
Name = param.Name;
|
||||
Type = param.Type;
|
||||
}
|
||||
|
||||
private static bool IsTypeValid(ScriptType type)
|
||||
{
|
||||
return SurfaceUtils.IsValidVisualScriptFunctionType(type) && !type.IsVoid;
|
||||
@@ -1654,6 +1661,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
base.OnSpawned();
|
||||
|
||||
// Setup initial signature
|
||||
var defaultSignature = _signature.Node == null;
|
||||
CheckFunctionName(ref _signature.Name);
|
||||
if (_signature.ReturnType == ScriptType.Null)
|
||||
_signature.ReturnType = new ScriptType(typeof(void));
|
||||
@@ -1661,8 +1669,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
SaveSignature();
|
||||
UpdateUI();
|
||||
|
||||
// Start editing
|
||||
OnEditSignature();
|
||||
if (defaultSignature)
|
||||
{
|
||||
// Start editing
|
||||
OnEditSignature();
|
||||
}
|
||||
|
||||
// Send event
|
||||
for (int i = 0; i < Surface.Nodes.Count; i++)
|
||||
@@ -1890,6 +1901,254 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class EventBaseNode : SurfaceNode, IFunctionsDependantNode
|
||||
{
|
||||
private ComboBoxElement _combobox;
|
||||
private Image _helperButton;
|
||||
private bool _isBind;
|
||||
private bool _isUpdateLocked = true;
|
||||
private List<string> _tooltips = new List<string>();
|
||||
private List<uint> _functionNodesIds = new List<uint>();
|
||||
private ScriptMemberInfo.Parameter[] _signature;
|
||||
|
||||
protected EventBaseNode(bool isBind, uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
_isBind = isBind;
|
||||
}
|
||||
|
||||
private bool IsValidFunctionSignature(ref VisualScriptFunctionNode.Signature sig)
|
||||
{
|
||||
if (!sig.ReturnType.IsVoid || sig.Parameters == null || sig.Parameters.Length != _signature.Length)
|
||||
return false;
|
||||
for (int i = 0; i < _signature.Length; i++)
|
||||
{
|
||||
if (_signature[i].Type != sig.Parameters[i].Type)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
_isUpdateLocked = true;
|
||||
if (_combobox == null)
|
||||
{
|
||||
_combobox = (ComboBoxElement)_children[4];
|
||||
_combobox.TooltipText = _isBind ? "Select the function to call when the event occurs" : "Select the function to unbind from the event";
|
||||
_combobox.SelectedIndexChanged += OnSelectedChanged;
|
||||
_helperButton = new Image
|
||||
{
|
||||
Location = _combobox.UpperRight + new Vector2(4, 3),
|
||||
Size = new Vector2(12.0f),
|
||||
Parent = this,
|
||||
};
|
||||
_helperButton.Clicked += OnHelperButtonClicked;
|
||||
}
|
||||
int toSelect = -1;
|
||||
var handlerFunctionNodeId = Convert.ToUInt32(Values[2]);
|
||||
_combobox.ClearItems();
|
||||
_tooltips.Clear();
|
||||
_functionNodesIds.Clear();
|
||||
var nodes = Surface.Nodes;
|
||||
var count = _signature != null ? nodes.Count : 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (nodes[i] is VisualScriptFunctionNode functionNode)
|
||||
{
|
||||
// Get if function signature matches the event signature
|
||||
functionNode.GetSignature(out var functionSig);
|
||||
if (IsValidFunctionSignature(ref functionSig))
|
||||
{
|
||||
if (functionNode.ID == handlerFunctionNodeId)
|
||||
toSelect = _functionNodesIds.Count;
|
||||
_functionNodesIds.Add(functionNode.ID);
|
||||
_tooltips.Add(functionNode.TooltipText);
|
||||
_combobox.AddItem(functionSig.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
_combobox.Tooltips = _tooltips.Count != 0 ? _tooltips.ToArray() : null;
|
||||
_combobox.Enabled = _tooltips.Count != 0;
|
||||
_combobox.SelectedIndex = toSelect;
|
||||
if (toSelect != -1)
|
||||
{
|
||||
_helperButton.Brush = new SpriteBrush(Editor.Instance.Icons.Search12);
|
||||
_helperButton.Color = Color.White;
|
||||
_helperButton.TooltipText = "Navigate to the handler function";
|
||||
}
|
||||
else if (_isBind)
|
||||
{
|
||||
_helperButton.Brush = new SpriteBrush(Editor.Instance.Icons.Add48);
|
||||
_helperButton.Color = Color.Red;
|
||||
_helperButton.TooltipText = "Add new handler function and bind it to this event";
|
||||
_helperButton.Enabled = _signature != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_helperButton.Enabled = false;
|
||||
}
|
||||
ResizeAuto();
|
||||
_isUpdateLocked = false;
|
||||
}
|
||||
|
||||
private void OnHelperButtonClicked(Image img, MouseButton mouseButton)
|
||||
{
|
||||
if (mouseButton != MouseButton.Left)
|
||||
return;
|
||||
if (_combobox.SelectedIndex != -1)
|
||||
{
|
||||
// Focus selected function
|
||||
var handlerFunctionNodeId = Convert.ToUInt32(Values[2]);
|
||||
var handlerFunctionNode = Surface.FindNode(handlerFunctionNodeId);
|
||||
Surface.FocusNode(handlerFunctionNode);
|
||||
}
|
||||
else if (_isBind)
|
||||
{
|
||||
// Create new function that matches the event signature
|
||||
var surfaceBounds = Surface.AllNodesBounds;
|
||||
Surface.ShowArea(new Rectangle(surfaceBounds.BottomLeft, new Vector2(200, 150)).MakeExpanded(400.0f));
|
||||
var node = Surface.Context.SpawnNode(16, 6, surfaceBounds.BottomLeft + new Vector2(0, 50), null, OnBeforeSpawnedNewHandler);
|
||||
Surface.Select(node);
|
||||
|
||||
// Bind this function
|
||||
SetValue(2, node.ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeforeSpawnedNewHandler(SurfaceNode node)
|
||||
{
|
||||
// Initialize signature to match the event
|
||||
var functionNode = (VisualScriptFunctionNode)node;
|
||||
functionNode._signature = new VisualScriptFunctionNode.Signature
|
||||
{
|
||||
Name = "On" + (string)Values[1],
|
||||
IsStatic = false,
|
||||
IsVirtual = false,
|
||||
Node = functionNode,
|
||||
ReturnType = ScriptType.Void,
|
||||
Parameters = new VisualScriptFunctionNode.Parameter[_signature.Length],
|
||||
};
|
||||
for (int i = 0; i < _signature.Length; i++)
|
||||
functionNode._signature.Parameters[i] = new VisualScriptFunctionNode.Parameter(ref _signature[i]);
|
||||
}
|
||||
|
||||
private void OnSelectedChanged(ComboBox cb)
|
||||
{
|
||||
if (_isUpdateLocked)
|
||||
return;
|
||||
var handlerFunctionNodeId = Convert.ToUInt32(Values[2]);
|
||||
var selectedID = cb.SelectedIndex != -1 ? _functionNodesIds[cb.SelectedIndex] : 0u;
|
||||
if (selectedID != handlerFunctionNodeId)
|
||||
{
|
||||
SetValue(2, selectedID);
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnFunctionCreated(SurfaceNode node)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void OnFunctionEdited(SurfaceNode node)
|
||||
{
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void OnFunctionDeleted(SurfaceNode node)
|
||||
{
|
||||
// Deselect if that function was selected
|
||||
var handlerFunctionNodeId = Convert.ToUInt32(Values[2]);
|
||||
if (node.ID == handlerFunctionNodeId)
|
||||
_combobox.SelectedIndex = -1;
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public override void OnSurfaceLoaded()
|
||||
{
|
||||
base.OnSurfaceLoaded();
|
||||
|
||||
// Find reflection information about event
|
||||
_signature = null;
|
||||
var isStatic = false;
|
||||
var eventName = (string)Values[1];
|
||||
var eventType = TypeUtils.GetType((string)Values[0]);
|
||||
var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
|
||||
if (member && SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
isStatic = member.IsStatic;
|
||||
_signature = member.GetParameters();
|
||||
TooltipText = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
}
|
||||
|
||||
// Setup instance box (static events don't need it)
|
||||
var instanceBox = GetBox(1);
|
||||
instanceBox.Visible = !isStatic;
|
||||
if (isStatic)
|
||||
instanceBox.RemoveConnections();
|
||||
else
|
||||
instanceBox.CurrentType = eventType;
|
||||
|
||||
_isUpdateLocked = false;
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
base.OnValuesChanged();
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_combobox = null;
|
||||
_helperButton = null;
|
||||
_tooltips.Clear();
|
||||
_tooltips = null;
|
||||
_functionNodesIds.Clear();
|
||||
_functionNodesIds = null;
|
||||
_signature = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BindEventNode : EventBaseNode
|
||||
{
|
||||
public BindEventNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(true, id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnSurfaceLoaded()
|
||||
{
|
||||
Title = "Bind " + (string)Values[1];
|
||||
|
||||
base.OnSurfaceLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UnbindEventNode : EventBaseNode
|
||||
{
|
||||
public UnbindEventNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(false, id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnSurfaceLoaded()
|
||||
{
|
||||
Title = "Unbind " + (string)Values[1];
|
||||
|
||||
base.OnSurfaceLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The nodes for that group.
|
||||
/// </summary>
|
||||
@@ -2031,6 +2290,50 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
null, // Default value
|
||||
},
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 9,
|
||||
Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch),
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Vector2(260, 60),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
string.Empty, // Event type
|
||||
string.Empty, // Event name
|
||||
(uint)0, // Handler function nodeId
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
|
||||
NodeElementArchetype.Factory.Input(2, "Instance", true, typeof(object), 1),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true),
|
||||
NodeElementArchetype.Factory.Text(2, 20, "Handler function:"),
|
||||
NodeElementArchetype.Factory.ComboBox(100, 20, 140),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 10,
|
||||
Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch),
|
||||
Title = string.Empty,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Vector2(260, 60),
|
||||
DefaultValues = new object[]
|
||||
{
|
||||
string.Empty, // Event type
|
||||
string.Empty, // Event name
|
||||
(uint)0, // Handler function nodeId
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
|
||||
NodeElementArchetype.Factory.Input(2, "Instance", true, typeof(object), 1),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true),
|
||||
NodeElementArchetype.Factory.Text(2, 20, "Handler function:"),
|
||||
NodeElementArchetype.Factory.ComboBox(100, 20, 140),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,10 @@ namespace FlaxEditor.Surface
|
||||
var titleLabelFont = Style.Current.FontLarge;
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
if (Children[i] is InputBox inputBox)
|
||||
var child = Children[i];
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
if (child is InputBox inputBox)
|
||||
{
|
||||
var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20;
|
||||
if (inputBox.DefaultValueEditor != null)
|
||||
@@ -195,12 +198,12 @@ namespace FlaxEditor.Surface
|
||||
leftWidth = Mathf.Max(leftWidth, boxWidth);
|
||||
leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f);
|
||||
}
|
||||
else if (Children[i] is OutputBox outputBox)
|
||||
else if (child is OutputBox outputBox)
|
||||
{
|
||||
rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20);
|
||||
rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f);
|
||||
}
|
||||
else if (Children[i] is Control control)
|
||||
else if (child is Control control)
|
||||
{
|
||||
if (control.AnchorPreset == AnchorPresets.TopLeft)
|
||||
{
|
||||
|
||||
@@ -406,6 +406,11 @@ namespace FlaxEditor.Surface
|
||||
return member.IsField && IsValidVisualScriptType(member.ValueType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptEvent(ScriptMemberInfo member)
|
||||
{
|
||||
return member.IsEvent && member.HasAttribute(typeof(UnmanagedAttribute));
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
||||
{
|
||||
if (scriptType.IsGenericType || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
|
||||
@@ -331,12 +331,13 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="typeID">The node archetype ID.</param>
|
||||
/// <param name="location">The location.</param>
|
||||
/// <param name="customValues">The custom values array. Must match node archetype <see cref="NodeArchetype.DefaultValues"/> size. Pass null to use default values.</param>
|
||||
/// <param name="beforeSpawned">The custom callback action to call after node creation but just before invoking spawn event. Can be used to initialize custom node data.</param>
|
||||
/// <returns>Created node.</returns>
|
||||
public SurfaceNode SpawnNode(ushort groupID, ushort typeID, Vector2 location, object[] customValues = null)
|
||||
public SurfaceNode SpawnNode(ushort groupID, ushort typeID, Vector2 location, object[] customValues = null, Action<SurfaceNode> beforeSpawned = null)
|
||||
{
|
||||
if (NodeFactory.GetArchetype(_surface.NodeArchetypes, groupID, typeID, out var groupArchetype, out var nodeArchetype))
|
||||
{
|
||||
return SpawnNode(groupArchetype, nodeArchetype, location, customValues);
|
||||
return SpawnNode(groupArchetype, nodeArchetype, location, customValues, beforeSpawned);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -348,8 +349,9 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="nodeArchetype">The node archetype.</param>
|
||||
/// <param name="location">The location.</param>
|
||||
/// <param name="customValues">The custom values array. Must match node archetype <see cref="NodeArchetype.DefaultValues"/> size. Pass null to use default values.</param>
|
||||
/// <param name="beforeSpawned">The custom callback action to call after node creation but just before invoking spawn event. Can be used to initialize custom node data.</param>
|
||||
/// <returns>Created node.</returns>
|
||||
public SurfaceNode SpawnNode(GroupArchetype groupArchetype, NodeArchetype nodeArchetype, Vector2 location, object[] customValues = null)
|
||||
public SurfaceNode SpawnNode(GroupArchetype groupArchetype, NodeArchetype nodeArchetype, Vector2 location, object[] customValues = null, Action<SurfaceNode> beforeSpawned = null)
|
||||
{
|
||||
if (groupArchetype == null || nodeArchetype == null)
|
||||
throw new ArgumentNullException();
|
||||
@@ -387,6 +389,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
node.Location = location;
|
||||
OnControlLoaded(node);
|
||||
beforeSpawned?.Invoke(node);
|
||||
node.OnSurfaceLoaded();
|
||||
OnControlSpawned(node);
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
//#define DEBUG_INVOKE_METHODS_SEARCHING
|
||||
//#define DEBUG_FIELDS_SEARCHING
|
||||
//#define DEBUG_EVENTS_SEARCHING
|
||||
|
||||
#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING || DEBUG_EVENTS_SEARCHING
|
||||
#define DEBUG_SEARCH_TIME
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -103,7 +108,7 @@ namespace FlaxEditor.Surface
|
||||
private static void OnActiveContextMenuShowAsync()
|
||||
{
|
||||
Profiler.BeginEvent("Setup Visual Script Context Menu (async)");
|
||||
#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING
|
||||
#if DEBUG_SEARCH_TIME
|
||||
var searchStartTime = DateTime.Now;
|
||||
var searchHitsCount = 0;
|
||||
#endif
|
||||
@@ -338,13 +343,65 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member.IsEvent)
|
||||
{
|
||||
var name = member.Name;
|
||||
|
||||
// Skip if searching by name doesn't return a match
|
||||
var members = scriptType.GetMembers(name, MemberTypes.Event, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
if (!members.Contains(member))
|
||||
continue;
|
||||
|
||||
// Check if field is valid for Visual Script usage
|
||||
if (SurfaceUtils.IsValidVisualScriptEvent(member))
|
||||
{
|
||||
var groupKey = new KeyValuePair<string, ushort>(scriptTypeName, 16);
|
||||
if (!_cache.TryGetValue(groupKey, out var group))
|
||||
{
|
||||
group = new GroupArchetype
|
||||
{
|
||||
GroupID = groupKey.Value,
|
||||
Name = groupKey.Key,
|
||||
Color = new Color(109, 160, 24),
|
||||
Tag = _version,
|
||||
Archetypes = new List<NodeArchetype>(),
|
||||
};
|
||||
_cache.Add(groupKey, group);
|
||||
}
|
||||
|
||||
// Add Bind event node
|
||||
var bindNode = (NodeArchetype)Archetypes.Function.Nodes[8].Clone();
|
||||
bindNode.DefaultValues[0] = scriptTypeTypeName;
|
||||
bindNode.DefaultValues[1] = name;
|
||||
bindNode.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
bindNode.Title = "Bind " + name;
|
||||
bindNode.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
bindNode.SubTitle = string.Format(" (in {0})", scriptTypeName);
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(bindNode);
|
||||
|
||||
// Add Unbind event node
|
||||
var unbindNode = (NodeArchetype)Archetypes.Function.Nodes[9].Clone();
|
||||
unbindNode.DefaultValues[0] = scriptTypeTypeName;
|
||||
unbindNode.DefaultValues[1] = name;
|
||||
unbindNode.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
unbindNode.Title = "Unbind " + name;
|
||||
unbindNode.Description = bindNode.Description;
|
||||
unbindNode.SubTitle = bindNode.SubTitle;
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(unbindNode);
|
||||
|
||||
#if DEBUG_EVENTS_SEARCHING
|
||||
Editor.LogWarning(scriptTypeTypeName + " -> " + member.GetSignature());
|
||||
searchHitsCount++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add group to context menu (on a main thread)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING
|
||||
#if DEBUG_SEARCH_TIME
|
||||
var addStartTime = DateTime.Now;
|
||||
#endif
|
||||
lock (_locker)
|
||||
@@ -352,12 +409,12 @@ namespace FlaxEditor.Surface
|
||||
_taskContextMenu.AddGroups(_cache.Values);
|
||||
_taskContextMenu = null;
|
||||
}
|
||||
#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING
|
||||
#if DEBUG_SEARCH_TIME
|
||||
Editor.LogError($"Added items to VisjectCM in: {(DateTime.Now - addStartTime).TotalMilliseconds} ms");
|
||||
#endif
|
||||
});
|
||||
|
||||
#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING
|
||||
#if DEBUG_SEARCH_TIME
|
||||
Editor.LogError($"Collected {searchHitsCount} items in: {(DateTime.Now - searchStartTime).TotalMilliseconds} ms");
|
||||
#endif
|
||||
Profiler.EndEvent();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Scripting/MException.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/Events.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MMethod.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||
@@ -347,7 +348,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
|
||||
const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID());
|
||||
if (param && instanceParams)
|
||||
{
|
||||
value = instanceParams->Value[paramIndex];
|
||||
value = instanceParams->Value.Params[paramIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -371,7 +372,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
|
||||
const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID());
|
||||
if (param && instanceParams)
|
||||
{
|
||||
instanceParams->Value[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero);
|
||||
instanceParams->Value.Params[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1004,6 +1005,125 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
if (returnedImpulse && returnedImpulse->HasConnection())
|
||||
eatBox(node, returnedImpulse->FirstConnection());
|
||||
break;
|
||||
}
|
||||
// Bind/Unbind
|
||||
case 9:
|
||||
case 10:
|
||||
{
|
||||
const bool bind = node->TypeID == 9;
|
||||
auto& stack = ThreadStacks.Get();
|
||||
if (!stack.Stack->Instance)
|
||||
{
|
||||
// TODO: add support for binding to events in static Visual Script
|
||||
LOG(Error, "Cannot bind to event in static Visual Script.");
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
const auto object = stack.Stack->Instance;
|
||||
|
||||
// Find method to bind
|
||||
VisualScriptGraphNode* methodNode = nullptr;
|
||||
const auto graph = stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr;
|
||||
if (graph)
|
||||
methodNode = graph->GetNode((uint32)node->Values[2]);
|
||||
if (!methodNode)
|
||||
{
|
||||
LOG(Error, "Missing function handler to bind to the event.");
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
VisualScript::Method* method = nullptr;
|
||||
for (auto& m : stack.Stack->Script->_methods)
|
||||
{
|
||||
if (m.Node == methodNode)
|
||||
{
|
||||
method = &m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!method)
|
||||
{
|
||||
LOG(Error, "Missing method to bind to the event.");
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find event
|
||||
const StringView eventTypeName(node->Values[0]);
|
||||
const StringView eventName(node->Values[1]);
|
||||
const StringAsANSI<100> eventTypeNameAnsi(eventTypeName.Get(), eventTypeName.Length());
|
||||
const ScriptingTypeHandle eventType = Scripting::FindScriptingType(StringAnsiView(eventTypeNameAnsi.Get(), eventTypeName.Length()));
|
||||
|
||||
// Find event binding callback
|
||||
auto eventBinder = ScriptingEvents::EventsTable.TryGet(Pair<ScriptingTypeHandle, StringView>(eventType, eventName));
|
||||
if (!eventBinder)
|
||||
{
|
||||
LOG(Error, "Cannot bind to missing event {0} from type {1}.", eventName, eventTypeName);
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
|
||||
// Evaluate object instance
|
||||
const auto box = node->GetBox(1);
|
||||
Variant instance;
|
||||
if (box->HasConnection())
|
||||
instance = eatBox(node, box->FirstConnection());
|
||||
else
|
||||
instance.SetObject(object);
|
||||
if (!instance.AsObject)
|
||||
{
|
||||
LOG(Error, "Cannot bind event to null object.");
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
// TODO: check if instance is of event type (including inheritance)
|
||||
|
||||
// Add Visual Script method to the event bindings table
|
||||
const auto& type = object->GetType();
|
||||
Guid id;
|
||||
if (Guid::Parse(type.Fullname, id))
|
||||
break;
|
||||
if (const auto visualScript = (VisualScript*)Content::GetAsset(id))
|
||||
{
|
||||
if (auto i = visualScript->GetScriptInstance(object))
|
||||
{
|
||||
VisualScript::EventBinding* eventBinding = nullptr;
|
||||
for (auto& b : i->EventBindings)
|
||||
{
|
||||
if (b.Type == eventType && b.Name == eventName)
|
||||
{
|
||||
eventBinding = &b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bind)
|
||||
{
|
||||
// Bind to the event
|
||||
if (!eventBinding)
|
||||
{
|
||||
eventBinding = &i->EventBindings.AddOne();
|
||||
eventBinding->Type = eventType;
|
||||
eventBinding->Name = eventName;
|
||||
}
|
||||
eventBinding->BindedMethods.Add(method);
|
||||
if (eventBinding->BindedMethods.Count() == 1)
|
||||
(*eventBinder)(instance.AsObject, object, true);
|
||||
}
|
||||
else if (eventBinding)
|
||||
{
|
||||
// Unbind from the event
|
||||
if (eventBinding->BindedMethods.Count() == 1)
|
||||
(*eventBinder)(instance.AsObject, object, false);
|
||||
eventBinding->BindedMethods.Remove(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call graph further
|
||||
const auto returnedImpulse = &node->Boxes[2];
|
||||
if (returnedImpulse && returnedImpulse->HasConnection())
|
||||
eatBox(node, returnedImpulse->FirstConnection());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@@ -1304,7 +1424,7 @@ Asset::LoadResult VisualScript::load()
|
||||
// Update instanced data from previous format to the current graph parameters scheme
|
||||
for (auto& e : _instances)
|
||||
{
|
||||
auto& instanceParams = e.Value;
|
||||
auto& instanceParams = e.Value.Params;
|
||||
Array<Variant> valuesCache(MoveTemp(instanceParams));
|
||||
instanceParams.Resize(count);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
@@ -1319,7 +1439,7 @@ Asset::LoadResult VisualScript::load()
|
||||
// Reset instances values to defaults
|
||||
for (auto& e : _instances)
|
||||
{
|
||||
auto& instanceParams = e.Value;
|
||||
auto& instanceParams = e.Value.Params;
|
||||
instanceParams.Resize(count);
|
||||
for (int32 i = 0; i < count; i++)
|
||||
instanceParams[i] = Graph.Parameters[i].Value;
|
||||
@@ -1421,13 +1541,17 @@ void VisualScript::CacheScriptingType()
|
||||
_scriptingTypeHandle = ScriptingTypeHandle(&binaryModule, typeIndex);
|
||||
binaryModule.Scripts.Add(this);
|
||||
|
||||
#if USE_EDITOR
|
||||
// When first Visual Script gets loaded register for other modules unload to clear runtime execution cache
|
||||
// Special initialization when the first Visual Script gets loaded
|
||||
if (typeIndex == 0)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Register for other modules unload to clear runtime execution cache
|
||||
Scripting::ScriptsReloading.Bind<VisualScriptingBinaryModule, &VisualScriptingBinaryModule::OnScriptsReloading>(&binaryModule);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Register for scripting events
|
||||
ScriptingEvents::Event.Bind(VisualScriptingBinaryModule::OnEvent);
|
||||
}
|
||||
}
|
||||
auto& type = _scriptingTypeHandle.Module->Types[_scriptingTypeHandle.TypeIndex];
|
||||
type.ManagedClass = baseType.GetType().ManagedClass;
|
||||
@@ -1550,7 +1674,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
|
||||
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
|
||||
|
||||
// Initialize instance data
|
||||
auto& instanceParams = visualScript->_instances[object->GetID()];
|
||||
auto& instanceParams = visualScript->_instances[object->GetID()].Params;
|
||||
instanceParams.Resize(visualScript->Graph.Parameters.Count());
|
||||
for (int32 i = 0; i < instanceParams.Count(); i++)
|
||||
instanceParams[i] = visualScript->Graph.Parameters[i].Value;
|
||||
@@ -1608,6 +1732,56 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
|
||||
|
||||
#endif
|
||||
|
||||
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant>& parameters, const ScriptingTypeHandle& eventType, const StringView& eventName)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
// Object event
|
||||
const auto& type = object->GetType();
|
||||
Guid id;
|
||||
if (Guid::Parse(type.Fullname, id))
|
||||
return;
|
||||
if (const auto visualScript = (VisualScript*)Content::GetAsset(id))
|
||||
{
|
||||
if (auto instance = visualScript->GetScriptInstance(object))
|
||||
{
|
||||
for (auto& b : instance->EventBindings)
|
||||
{
|
||||
if (b.Type != eventType || b.Name != eventName)
|
||||
continue;
|
||||
for (auto& m : b.BindedMethods)
|
||||
{
|
||||
VisualScripting::Invoke(m, object, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Static event
|
||||
for (auto& asset : Content::GetAssetsRaw())
|
||||
{
|
||||
if (const auto visualScript = ScriptingObject::Cast<VisualScript>(asset.Value))
|
||||
{
|
||||
for (auto& e : visualScript->_instances)
|
||||
{
|
||||
auto instance = &e.Value;
|
||||
for (auto& b : instance->EventBindings)
|
||||
{
|
||||
if (b.Type != eventType || b.Name != eventName)
|
||||
continue;
|
||||
for (auto& m : b.BindedMethods)
|
||||
{
|
||||
VisualScripting::Invoke(m, object, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const StringAnsi& VisualScriptingBinaryModule::GetName() const
|
||||
{
|
||||
return _name;
|
||||
@@ -1702,7 +1876,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
|
||||
LOG(Error, "Missing parameters for the object instance.");
|
||||
return true;
|
||||
}
|
||||
result = instanceParams->Value[vsFiled->Index];
|
||||
result = instanceParams->Value.Params[vsFiled->Index];
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1721,7 +1895,7 @@ bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& inst
|
||||
LOG(Error, "Missing parameters for the object instance.");
|
||||
return true;
|
||||
}
|
||||
instanceParams->Value[vsFiled->Index] = value;
|
||||
instanceParams->Value.Params[vsFiled->Index] = value;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1735,7 +1909,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
|
||||
const auto instanceParams = asset->_instances.Find(object->GetID());
|
||||
if (instanceParams)
|
||||
{
|
||||
auto& params = instanceParams->Value;
|
||||
auto& params = instanceParams->Value.Params;
|
||||
if (otherObj)
|
||||
{
|
||||
// Serialize parameters diff
|
||||
@@ -1746,7 +1920,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
|
||||
{
|
||||
auto& param = asset->Graph.Parameters[paramIndex];
|
||||
auto& value = params[paramIndex];
|
||||
auto& otherValue = otherParams->Value[paramIndex];
|
||||
auto& otherValue = otherParams->Value.Params[paramIndex];
|
||||
if (value != otherValue)
|
||||
{
|
||||
param.Identifier.ToString(idName, Guid::FormatType::N);
|
||||
@@ -1798,7 +1972,7 @@ void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeSt
|
||||
if (instanceParams)
|
||||
{
|
||||
// Deserialize all parameters
|
||||
auto& params = instanceParams->Value;
|
||||
auto& params = instanceParams->Value.Params;
|
||||
for (auto i = stream.MemberBegin(); i != stream.MemberEnd(); ++i)
|
||||
{
|
||||
StringAnsiView idNameAnsi(i->name.GetString(), i->name.GetStringLength());
|
||||
@@ -1865,6 +2039,11 @@ ScriptingObject* VisualScript::CreateInstance()
|
||||
return scriptingTypeHandle ? scriptingTypeHandle.GetType().Script.Spawn(ScriptingObjectSpawnParams(Guid::New(), scriptingTypeHandle)) : nullptr;
|
||||
}
|
||||
|
||||
VisualScript::Instance* VisualScript::GetScriptInstance(ScriptingObject* instance) const
|
||||
{
|
||||
return instance ? _instances.TryGet(instance->GetID()) : nullptr;
|
||||
}
|
||||
|
||||
Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance) const
|
||||
{
|
||||
CHECK_RETURN(instance, Variant());
|
||||
@@ -1874,7 +2053,7 @@ Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, Sc
|
||||
{
|
||||
const auto instanceParams = _instances.Find(instance->GetID());
|
||||
if (instanceParams)
|
||||
return instanceParams->Value[paramIndex];
|
||||
return instanceParams->Value.Params[paramIndex];
|
||||
LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name);
|
||||
return Graph.Parameters[paramIndex].Value;
|
||||
}
|
||||
@@ -1893,7 +2072,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
|
||||
const auto instanceParams = _instances.Find(instance->GetID());
|
||||
if (instanceParams)
|
||||
{
|
||||
instanceParams->Value[paramIndex] = value;
|
||||
instanceParams->Value.Params[paramIndex] = value;
|
||||
return;
|
||||
}
|
||||
LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name);
|
||||
@@ -1913,7 +2092,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
|
||||
const auto instanceParams = _instances.Find(instance->GetID());
|
||||
if (instanceParams)
|
||||
{
|
||||
instanceParams->Value[paramIndex] = MoveTemp(value);
|
||||
instanceParams->Value.Params[paramIndex] = MoveTemp(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,22 @@ public:
|
||||
StringAnsi Name;
|
||||
};
|
||||
|
||||
struct EventBinding
|
||||
{
|
||||
ScriptingTypeHandle Type;
|
||||
String Name;
|
||||
Array<Method*, InlinedAllocation<4>> BindedMethods;
|
||||
};
|
||||
|
||||
struct Instance
|
||||
{
|
||||
Array<Variant> Params;
|
||||
Array<EventBinding> EventBindings;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Dictionary<Guid, Array<Variant>> _instances;
|
||||
Dictionary<Guid, Instance> _instances;
|
||||
ScriptingTypeHandle _scriptingTypeHandle;
|
||||
ScriptingTypeHandle _scriptingTypeHandleCached;
|
||||
StringAnsiView _typename;
|
||||
@@ -179,6 +192,13 @@ public:
|
||||
/// <returns>The created instance or null if failed.</returns>
|
||||
API_FUNCTION() ScriptingObject* CreateInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Visual Script instance data.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object instance.</param>
|
||||
/// <returns>The data or invalid instance (not VS or missing).</returns>
|
||||
Instance* GetScriptInstance(ScriptingObject* instance) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the Visual Script parameter of the given instance.
|
||||
/// </summary>
|
||||
@@ -307,6 +327,7 @@ private:
|
||||
#if USE_EDITOR
|
||||
void OnScriptsReloading();
|
||||
#endif
|
||||
static void OnEvent(ScriptingObject* object, Span<Variant>& parameters, const ScriptingTypeHandle& eventType, const StringView& eventName);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "MException.h"
|
||||
#include "Scripting.h"
|
||||
#include "StdTypesContainer.h"
|
||||
#include "Events.h"
|
||||
|
||||
Dictionary<Pair<ScriptingTypeHandle, StringView>, void(*)(ScriptingObject*, void*, bool)> ScriptingEvents::EventsTable;
|
||||
Delegate<ScriptingObject*, Span<Variant>&, const ScriptingTypeHandle&, const StringView&> ScriptingEvents::Event;
|
||||
|
||||
ManagedBinaryModule* GetBinaryModuleCorlib()
|
||||
{
|
||||
|
||||
36
Source/Engine/Scripting/Events.h
Normal file
36
Source/Engine/Scripting/Events.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ScriptingType.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Core/Types/Variant.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
/// <summary>
|
||||
/// The helper utility for binding and invoking scripting events (eg. used by Visual Scripting).
|
||||
/// </summary>
|
||||
class FLAXENGINE_API ScriptingEvents
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Global table for registered even binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Key: pair of event type name (full), event name.
|
||||
/// Value: event binder function with parameters: event caller instance (null for static events), object to bind, true to bind/false to unbind.
|
||||
/// </remarks>
|
||||
static Dictionary<Pair<ScriptingTypeHandle, StringView>, void(*)(ScriptingObject*, void*, bool)> EventsTable;
|
||||
|
||||
/// <summary>
|
||||
/// The action called when any scripting event occurs. Can be used to invoke scripting code that binded to this particular event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Delegate parameters: event caller instance (null for static events), event invocation parameters list, event type name (full), event name.
|
||||
/// </remarks>
|
||||
static Delegate<ScriptingObject*, Span<Variant>&, const ScriptingTypeHandle&, const StringView&> Event;
|
||||
};
|
||||
@@ -1091,19 +1091,19 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
if (!useScripting)
|
||||
continue;
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h");
|
||||
var paramsCount = eventInfo.Type.GenericArgs.Count;
|
||||
|
||||
// C# event invoking wrapper (calls C# event from C++ delegate)
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h");
|
||||
contents.Append(" ");
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name);
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]);
|
||||
contents.Append(" arg" + i);
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i);
|
||||
}
|
||||
contents.Append(')').AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
@@ -1112,11 +1112,11 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, eventInfo.Type.GenericArgs.Count).AppendLine();
|
||||
contents.Append(" CHECK(mmethod);").AppendLine();
|
||||
contents.Append(" MonoObject* exception = nullptr;").AppendLine();
|
||||
if (eventInfo.Type.GenericArgs.Count == 0)
|
||||
if (paramsCount == 0)
|
||||
contents.AppendLine(" void** params = nullptr;");
|
||||
else
|
||||
contents.AppendLine($" void* params[{eventInfo.Type.GenericArgs.Count}];");
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
contents.AppendLine($" void* params[{paramsCount}];");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
var paramType = eventInfo.Type.GenericArgs[i];
|
||||
var paramName = "arg" + i;
|
||||
@@ -1139,7 +1139,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append("bool bind)").AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
contents.Append(" Function<void(");
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
@@ -1156,6 +1156,56 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(" else").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Unbind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
|
||||
// Generic scripting event invoking wrapper (calls scripting code from C++ delegate)
|
||||
CppIncludeFiles.Add("Engine/Scripting/Events.h");
|
||||
contents.Append(" ");
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.AppendFormat("void {0}_Wrapper(", eventInfo.Name);
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i);
|
||||
}
|
||||
contents.Append(')').AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
if (paramsCount == 0)
|
||||
contents.AppendLine(" Variant* params = nullptr;");
|
||||
else
|
||||
contents.AppendLine($" Variant params[{eventInfo.Type.GenericArgs.Count}];");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
var paramType = eventInfo.Type.GenericArgs[i];
|
||||
var paramName = "arg" + i;
|
||||
var paramValue = GenerateCppWrapperNativeToVariant(buildData, paramType, classInfo, paramName);
|
||||
contents.Append($" params[{i}] = {paramValue};").AppendLine();
|
||||
}
|
||||
contents.AppendLine($" ScriptingEvents::Event({(eventInfo.IsStatic ? "nullptr" : "(ScriptingObject*)this")}, Span<Variant>(params, {paramsCount}), {classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}));");
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
|
||||
// Scripting event wrapper binding method (binds/unbinds generic wrapper to C++ delegate)
|
||||
contents.AppendFormat(" static void {0}_Bind(", eventInfo.Name);
|
||||
contents.AppendFormat("{0}* obj, void* instance, bool bind)", classTypeNameNative).AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
contents.Append(" Function<void(");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]);
|
||||
}
|
||||
contents.Append(")> f;").AppendLine();
|
||||
if (eventInfo.IsStatic)
|
||||
contents.AppendFormat(" f.Bind<{0}_Wrapper>();", eventInfo.Name).AppendLine();
|
||||
else
|
||||
contents.AppendFormat(" f.Bind<{1}Internal, &{1}Internal::{0}_Wrapper>(({1}Internal*)instance);", eventInfo.Name, classTypeNameInternal).AppendLine();
|
||||
contents.Append(" if (bind)").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Bind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" else").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Unbind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
}
|
||||
|
||||
// Fields
|
||||
@@ -1299,6 +1349,9 @@ namespace Flax.Build.Bindings
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);");
|
||||
|
||||
// Register scripting event binder
|
||||
contents.AppendLine($" ScriptingEvents::EventsTable[Pair<ScriptingTypeHandle, StringView>({classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}))] = (void(*)(ScriptingObject*, void*, bool)){classTypeNameInternal}Internal::{eventInfo.Name}_Bind;");
|
||||
}
|
||||
foreach (var fieldInfo in classInfo.Fields)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user