From 038a3603e41f4d44323655657607dfc447967253 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sun, 24 Sep 2023 19:33:03 +0200 Subject: [PATCH 01/23] - Possible nodes get filtered now depending on the available node ports --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 14 ++++-- .../Surface/ContextMenu/VisjectCMGroup.cs | 39 ++++++++++++++--- .../Surface/ContextMenu/VisjectCMItem.cs | 43 ++++++++++++++++++- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index bae62556e..24cb29cb9 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -419,7 +419,7 @@ namespace FlaxEditor.Surface.ContextMenu Profiler.BeginEvent("VisjectCM.OnSearchFilterChanged"); - if (string.IsNullOrEmpty(_searchBox.Text)) + if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null) { ResetView(); Profiler.EndEvent(); @@ -430,7 +430,7 @@ namespace FlaxEditor.Surface.ContextMenu LockChildrenRecursive(); for (int i = 0; i < _groups.Count; i++) { - _groups[i].UpdateFilter(_searchBox.Text); + _groups[i].UpdateFilter(_searchBox.Text, _selectedBox); _groups[i].UpdateItemSort(_selectedBox); } SortGroups(); @@ -503,12 +503,19 @@ namespace FlaxEditor.Surface.ContextMenu _searchBox.Clear(); SelectedItem = null; for (int i = 0; i < _groups.Count; i++) + { _groups[i].ResetView(); + } UnlockChildrenRecursive(); SortGroups(); PerformLayout(); + for (int i = 0; i < _groups.Count; i++) + { + _groups[i].EvaluateVisibilityWithBox(_selectedBox); + } + Profiler.EndEvent(); } @@ -626,10 +633,11 @@ namespace FlaxEditor.Surface.ContextMenu // Prepare UpdateSurfaceParametersGroup(); ResetView(); + _panel1.VScrollBar.TargetValue = 0; Focus(); _waitingForInput = true; - + base.OnShow(); } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index 446840f2c..cd189ab94 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -66,7 +66,7 @@ namespace FlaxEditor.Surface.ContextMenu { if (_children[i] is VisjectCMItem item) { - item.UpdateFilter(null); + item.UpdateFilter(null, null); item.UpdateScore(null); } } @@ -84,7 +84,7 @@ namespace FlaxEditor.Surface.ContextMenu /// Updates the filter. /// /// The filter text. - public void UpdateFilter(string filterText) + public void UpdateFilter(string filterText, Box selectedBox) { Profiler.BeginEvent("VisjectCMGroup.UpdateFilter"); @@ -94,13 +94,13 @@ namespace FlaxEditor.Surface.ContextMenu { if (_children[i] is VisjectCMItem item) { - item.UpdateFilter(filterText); + item.UpdateFilter(filterText, selectedBox); isAnyVisible |= item.Visible; } } // Update header title - if (QueryFilterHelper.Match(filterText, HeaderText)) + /*if (QueryFilterHelper.Match(filterText, HeaderText)) { for (int i = 0; i < _children.Count; i++) { @@ -110,7 +110,7 @@ namespace FlaxEditor.Surface.ContextMenu } } isAnyVisible = true; - } + }*/ // Update itself if (isAnyVisible) @@ -128,6 +128,35 @@ namespace FlaxEditor.Surface.ContextMenu Profiler.EndEvent(); } + public void EvaluateVisibilityWithBox(Box selectedBox) + { + if (selectedBox == null) + { + Visible = true; + return; + } + + bool isAnyVisible = false; + for (int i = 0; i < _children.Count; i++) + { + if (_children[i] is VisjectCMItem item) + { + isAnyVisible |= item.IsCompatibleWithBox(selectedBox); + } + } + + // Update itself + if (isAnyVisible) + { + Visible = true; + } + else + { + // Hide group if none of the items matched the filter + Visible = false; + } + } + /// /// Updates the sorting of the s of this /// Also updates the diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index b68d529a1..b2d5fd7a8 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -113,12 +113,51 @@ namespace FlaxEditor.Surface.ContextMenu return false; } + public bool IsCompatibleWithBox(Box box) + { + if (box == null) + return true; + + if(_archetype?.Elements == null) + return false; + + bool isCompatible = false; + foreach (NodeElementArchetype element in _archetype.Elements) + { + if(element.Type != NodeElementType.Output && element.Type != NodeElementType.Input) + continue; + + if ((box.IsOutput && element.Type == NodeElementType.Output) || (!box.IsOutput && element.Type == NodeElementType.Input)) + continue; + + bool checkCompatibility = box.CanUseType(element.ConnectionsType);; + if (!checkCompatibility) + { + if ((element.ConnectionsType == null || element.ConnectionsType == typeof(void)) && box.CurrentType != typeof(FlaxEngine.Object)) + checkCompatibility = true; + } + isCompatible |= checkCompatibility; + + /*if(!isCompatible) + Debug.Log($"Is {_archetype.Title} cant connect type {element.ConnectionsType}");*/ + } + + Visible = isCompatible; + return isCompatible; + } + /// /// Updates the filter. /// /// The filter text. - public void UpdateFilter(string filterText) + public void UpdateFilter(string filterText, Box selectedBox) { + if (selectedBox != null) + { + if (!IsCompatibleWithBox(selectedBox)) + return; + } + _isStartsWithMatch = _isFullMatch = false; if (filterText == null) { @@ -192,7 +231,7 @@ namespace FlaxEditor.Surface.ContextMenu } } } - + /// public override void Draw() { From 8d39d51f900dd879f2975b299c2e192050508f97 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sun, 24 Sep 2023 20:22:44 +0200 Subject: [PATCH 02/23] - Added profiling --- Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs | 4 ++++ Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index cd189ab94..9b5da5913 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -136,6 +136,8 @@ namespace FlaxEditor.Surface.ContextMenu return; } + Profiler.BeginEvent("VisjectCMGroup.EvaluateVisibilityWithBox"); + bool isAnyVisible = false; for (int i = 0; i < _children.Count; i++) { @@ -155,6 +157,8 @@ namespace FlaxEditor.Surface.ContextMenu // Hide group if none of the items matched the filter Visible = false; } + + Profiler.EndEvent(); } /// diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index b2d5fd7a8..dc13a0e2b 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -121,6 +121,8 @@ namespace FlaxEditor.Surface.ContextMenu if(_archetype?.Elements == null) return false; + Profiler.BeginEvent("VisjectCMItem.IsCompatibleWithBox"); + bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) { @@ -129,11 +131,11 @@ namespace FlaxEditor.Surface.ContextMenu if ((box.IsOutput && element.Type == NodeElementType.Output) || (!box.IsOutput && element.Type == NodeElementType.Input)) continue; - - bool checkCompatibility = box.CanUseType(element.ConnectionsType);; + + bool checkCompatibility = ((element.ConnectionsType == null || element.ConnectionsType == typeof(void)) && box.CurrentType != typeof(FlaxEngine.Object)); if (!checkCompatibility) { - if ((element.ConnectionsType == null || element.ConnectionsType == typeof(void)) && box.CurrentType != typeof(FlaxEngine.Object)) + if (box.CanUseType(element.ConnectionsType)) checkCompatibility = true; } isCompatible |= checkCompatibility; @@ -143,6 +145,8 @@ namespace FlaxEditor.Surface.ContextMenu } Visible = isCompatible; + + Profiler.EndEvent(); return isCompatible; } From 50ebd5cb8727a3d54fcde07f5cf3f30ffa50d153 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sun, 24 Sep 2023 20:31:22 +0200 Subject: [PATCH 03/23] - Fixed massive UI freeze because of perform layout oversight --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 24cb29cb9..3a3dfe027 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -506,15 +506,14 @@ namespace FlaxEditor.Surface.ContextMenu { _groups[i].ResetView(); } - UnlockChildrenRecursive(); - - SortGroups(); - PerformLayout(); - for (int i = 0; i < _groups.Count; i++) { _groups[i].EvaluateVisibilityWithBox(_selectedBox); } + UnlockChildrenRecursive(); + + SortGroups(); + PerformLayout(); Profiler.EndEvent(); } From 33c51d0a8eaac7ec6190d3c784b4189baadfd873 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Mon, 25 Sep 2023 19:10:05 +0200 Subject: [PATCH 04/23] - Filtering is functioning now for all default groups --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 3 - .../Surface/ContextMenu/VisjectCMGroup.cs | 2 +- .../Surface/ContextMenu/VisjectCMItem.cs | 110 ++++++++++++++++-- 3 files changed, 99 insertions(+), 16 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 3a3dfe027..1cec9554b 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -505,9 +505,6 @@ namespace FlaxEditor.Surface.ContextMenu for (int i = 0; i < _groups.Count; i++) { _groups[i].ResetView(); - } - for (int i = 0; i < _groups.Count; i++) - { _groups[i].EvaluateVisibilityWithBox(_selectedBox); } UnlockChildrenRecursive(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index 9b5da5913..9560aecbb 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -143,7 +143,7 @@ namespace FlaxEditor.Surface.ContextMenu { if (_children[i] is VisjectCMItem item) { - isAnyVisible |= item.IsCompatibleWithBox(selectedBox); + isAnyVisible |= item.CanConnectTo(selectedBox); } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index dc13a0e2b..4030017a7 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Utilities; using FlaxEngine; @@ -113,31 +114,45 @@ namespace FlaxEditor.Surface.ContextMenu return false; } - public bool IsCompatibleWithBox(Box box) + public bool CanConnectTo(Box startBox) { - if (box == null) + if (startBox == null) return true; if(_archetype?.Elements == null) return false; - Profiler.BeginEvent("VisjectCMItem.IsCompatibleWithBox"); - bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) { if(element.Type != NodeElementType.Output && element.Type != NodeElementType.Input) continue; - if ((box.IsOutput && element.Type == NodeElementType.Output) || (!box.IsOutput && element.Type == NodeElementType.Input)) + if ((startBox.IsOutput && element.Type == NodeElementType.Output) || (!startBox.IsOutput && element.Type == NodeElementType.Input)) continue; - - bool checkCompatibility = ((element.ConnectionsType == null || element.ConnectionsType == typeof(void)) && box.CurrentType != typeof(FlaxEngine.Object)); - if (!checkCompatibility) + + ScriptType inType; + ScriptType outType; + ConnectionsHint hint; + if (startBox.IsOutput) { - if (box.CanUseType(element.ConnectionsType)) - checkCompatibility = true; + inType = element.ConnectionsType; + outType = startBox.CurrentType; + hint = _archetype.ConnectionsHints; } + else + { + inType = startBox.CurrentType; + outType = element.ConnectionsType; + hint = startBox.ParentNode.Archetype.ConnectionsHints; + } + + bool checkCompatibility = CanCastToType(inType, outType, hint); + /*if (!checkCompatibility) + { + /*checkCompatibility = element.ConnectionsType == null && startBox.CurrentType != ScriptType.Object;#1# + checkCompatibility = + }*/ isCompatible |= checkCompatibility; /*if(!isCompatible) @@ -146,9 +161,80 @@ namespace FlaxEditor.Surface.ContextMenu Visible = isCompatible; - Profiler.EndEvent(); return isCompatible; } + + private bool CanCastToType(ScriptType currentType, ScriptType type, ConnectionsHint hint) + { + if (VisjectSurface.CanUseDirectCastStatic(type, currentType, false)) + return true; + + var connectionsHints = hint; + if (currentType == ScriptType.Null && connectionsHints != ConnectionsHint.None) + { + if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything) + return true; + if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void)) + return true; + if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum) + return true; + if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray) + return true; + if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary) + return true; + if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector) + { + var t = type.Type; + if (t == typeof(Vector2) || + t == typeof(Vector3) || + t == typeof(Vector4) || + t == typeof(Float2) || + t == typeof(Float3) || + t == typeof(Float4) || + t == typeof(Double2) || + t == typeof(Double3) || + t == typeof(Double4) || + t == typeof(Int2) || + t == typeof(Int3) || + t == typeof(Int4) || + t == typeof(Color)) + { + return true; + } + } + if ((connectionsHints & ConnectionsHint.Scalar) == ConnectionsHint.Scalar) + { + var t = type.Type; + if (t == typeof(bool) || + t == typeof(char) || + t == typeof(byte) || + t == typeof(short) || + t == typeof(ushort) || + t == typeof(int) || + t == typeof(uint) || + t == typeof(long) || + t == typeof(ulong) || + t == typeof(float) || + t == typeof(double)) + { + return true; + } + } + } + + return CanCast(type, currentType); + } + + private static bool CanCast(ScriptType oB, ScriptType iB) + { + if (oB == iB) + return true; + if (oB == ScriptType.Null || iB == ScriptType.Null) + return false; + return (oB.Type != typeof(void) && oB.Type != typeof(FlaxEngine.Object)) && + (iB.Type != typeof(void) && iB.Type != typeof(FlaxEngine.Object)) && + oB.IsAssignableFrom(iB); + } /// /// Updates the filter. @@ -158,7 +244,7 @@ namespace FlaxEditor.Surface.ContextMenu { if (selectedBox != null) { - if (!IsCompatibleWithBox(selectedBox)) + if (!CanConnectTo(selectedBox)) return; } From 3b393ef4db27e2ccb629ba006646be973ba2fe0a Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Mon, 25 Sep 2023 21:58:05 +0200 Subject: [PATCH 05/23] Groups now get filtered by box type when added after the context menu was opened --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 5 +++++ .../Editor/Surface/ContextMenu/VisjectCMItem.cs | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 1cec9554b..04130fc1e 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -288,6 +288,10 @@ namespace FlaxEditor.Surface.ContextMenu OnSearchFilterChanged(); } } + else + { + group.EvaluateVisibilityWithBox(_selectedBox); + } Profiler.EndEvent(); } @@ -321,6 +325,7 @@ namespace FlaxEditor.Surface.ContextMenu Parent = group }; } + group.EvaluateVisibilityWithBox(_selectedBox); group.SortChildren(); group.Parent = _groupsPanel; _groups.Add(group); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 4030017a7..00854ef5a 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -117,10 +117,23 @@ namespace FlaxEditor.Surface.ContextMenu public bool CanConnectTo(Box startBox) { if (startBox == null) - return true; + { + Visible = true; + return true; + } + + if (_archetype.Title == "GetChild") + { + Debug.Log(""); + Debug.Log(_archetype.Create == null); + Debug.Log(_archetype.Create.GetType() == typeof(FlaxEditor.Surface.Archetypes.Function.MethodOverrideNode)); + } - if(_archetype?.Elements == null) + if (_archetype?.Elements == null) + { + Visible = false; return false; + } bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) From d8e2b06c3828e53cea73ee98f4183166c54bcbb7 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Mon, 25 Sep 2023 23:26:33 +0200 Subject: [PATCH 06/23] - Minor cleanup --- .../Editor/Surface/ContextMenu/VisjectCMItem.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 00854ef5a..7b0ce6c85 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -121,13 +121,6 @@ namespace FlaxEditor.Surface.ContextMenu Visible = true; return true; } - - if (_archetype.Title == "GetChild") - { - Debug.Log(""); - Debug.Log(_archetype.Create == null); - Debug.Log(_archetype.Create.GetType() == typeof(FlaxEditor.Surface.Archetypes.Function.MethodOverrideNode)); - } if (_archetype?.Elements == null) { @@ -161,15 +154,7 @@ namespace FlaxEditor.Surface.ContextMenu } bool checkCompatibility = CanCastToType(inType, outType, hint); - /*if (!checkCompatibility) - { - /*checkCompatibility = element.ConnectionsType == null && startBox.CurrentType != ScriptType.Object;#1# - checkCompatibility = - }*/ isCompatible |= checkCompatibility; - - /*if(!isCompatible) - Debug.Log($"Is {_archetype.Title} cant connect type {element.ConnectionsType}");*/ } Visible = isCompatible; From c4da34a463c5d0d88018154a83dcf7f5cd710ff7 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Wed, 27 Sep 2023 16:24:33 +0200 Subject: [PATCH 07/23] - Implemented function node and bind/unbind node compatiblility/filtering - Added NodeTypeHint enum --- Source/Editor/Surface/Archetypes/Function.cs | 10 +++ .../Surface/ContextMenu/VisjectCMItem.cs | 74 ++++++++++++++++--- Source/Editor/Surface/NodeArchetype.cs | 20 +++++ Source/Editor/Surface/VisualScriptSurface.cs | 3 +- 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index cffb2ad6f..d027dd8f1 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -2228,6 +2228,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Function Input", Description = "The graph function input data", Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaPaste, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2247,6 +2248,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Function Output", Description = "The graph function output data", Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaPaste, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2265,6 +2267,7 @@ namespace FlaxEditor.Surface.Archetypes Title = string.Empty, Description = "Overrides the base class method with custom implementation", Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2279,6 +2282,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new InvokeMethodNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2302,6 +2306,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new ReturnNode(id, context, arch, groupArch), Title = "Return", Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(100, 40), DefaultValues = new object[] { @@ -2320,6 +2325,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "New Function", Description = "Adds a new function to the script", Flags = NodeFlags.VisualScriptGraph, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 20), DefaultValues = new object[] { @@ -2332,6 +2338,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new GetFieldNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2347,6 +2354,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new SetFieldNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2363,6 +2371,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(260, 60), DefaultValues = new object[] { @@ -2385,6 +2394,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(260, 60), DefaultValues = new object[] { diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 7b0ce6c85..f8b7d9d51 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Utilities; namespace FlaxEditor.Surface.ContextMenu { @@ -121,13 +123,70 @@ namespace FlaxEditor.Surface.ContextMenu Visible = true; return true; } - - if (_archetype?.Elements == null) + + if (_archetype == null) { Visible = false; return false; } - + + bool isCompatible = false; + if (_archetype.Elements != null) + { + isCompatible = CheckElementsCompatibility(startBox); + } + + if (_archetype.NodeTypeHint == NodeTypeHint.FunctionNode) + { + isCompatible = false; + ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; + + if (_archetype.Tag is ScriptMemberInfo info) + { + memberInfo = info; + } + else if(_archetype.DefaultValues is { Length: > 1 }) + { + var eventName = (string)_archetype.DefaultValues[1]; + var eventType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); + memberInfo = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + } + + if (memberInfo != ScriptMemberInfo.Null) + { + if (startBox.IsOutput) + { + var parameters = memberInfo.GetParameters(); + ScriptType outType = startBox.CurrentType; + + if (!memberInfo.IsStatic) + { + var scriptType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); + isCompatible |= CanCastToType(scriptType, outType, _archetype.ConnectionsHints); + } + + if (!memberInfo.IsEvent) + { + for (int i = 0; i < parameters.Length; i++) + { + ScriptType inType = parameters[i].Type; + isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); + } + } + } + else + { + + } + } + } + + Visible = isCompatible; + return isCompatible; + } + + private bool CheckElementsCompatibility(Box startBox) + { bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) { @@ -153,15 +212,12 @@ namespace FlaxEditor.Surface.ContextMenu hint = startBox.ParentNode.Archetype.ConnectionsHints; } - bool checkCompatibility = CanCastToType(inType, outType, hint); - isCompatible |= checkCompatibility; + isCompatible |= CanCastToType(inType, outType, hint); } - - Visible = isCompatible; - + return isCompatible; } - + private bool CanCastToType(ScriptType currentType, ScriptType type, ConnectionsHint hint) { if (VisjectSurface.CanUseDirectCastStatic(type, currentType, false)) diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index 6dc923ce2..1cdaee2d2 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -73,6 +73,20 @@ namespace FlaxEditor.Surface All = Scalar | Vector | Enum | Anything | Value | Array | Dictionary, } + [HideInEditor] + public enum NodeTypeHint + { + /// + /// Is Node. + /// + Default = 0, + + /// + /// Is Function Node. + /// + FunctionNode = 1, + } + /// /// Surface node archetype description. /// @@ -152,6 +166,11 @@ namespace FlaxEditor.Surface /// public ConnectionsHint ConnectionsHints; + /// + /// Node Type hints. + /// + public NodeTypeHint NodeTypeHint; + /// /// Array with independent boxes IDs. /// @@ -193,6 +212,7 @@ namespace FlaxEditor.Surface DefaultValues = (object[])DefaultValues?.Clone(), DefaultType = DefaultType, ConnectionsHints = ConnectionsHints, + NodeTypeHint = NodeTypeHint, IndependentBoxes = (int[])IndependentBoxes?.Clone(), DependentBoxes = (int[])DependentBoxes?.Clone(), Elements = (NodeElementArchetype[])Elements?.Clone(), diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index e961f686d..b6c0112a9 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -370,7 +370,7 @@ namespace FlaxEditor.Surface bindNode.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); bindNode.SubTitle = string.Format(" (in {0})", scriptTypeName); ((IList)group.Archetypes).Add(bindNode); - + // Add Unbind event node var unbindNode = (NodeArchetype)Archetypes.Function.Nodes[9].Clone(); unbindNode.DefaultValues[0] = scriptTypeTypeName; @@ -589,6 +589,7 @@ namespace FlaxEditor.Surface node.DefaultValues[0] = name; node.DefaultValues[1] = parameters.Length; node.Title = "Override " + name; + node.Tag = member; nodes.Add(node); } } From 63c213aec010198d8d750d528b93ec93db46ef20 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Wed, 27 Sep 2023 17:04:35 +0200 Subject: [PATCH 08/23] - Support for Impulse ports --- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index f8b7d9d51..370c5014a 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -138,7 +138,6 @@ namespace FlaxEditor.Surface.ContextMenu if (_archetype.NodeTypeHint == NodeTypeHint.FunctionNode) { - isCompatible = false; ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; if (_archetype.Tag is ScriptMemberInfo info) @@ -154,11 +153,17 @@ namespace FlaxEditor.Surface.ContextMenu if (memberInfo != ScriptMemberInfo.Null) { + if(memberInfo.IsEvent) + isCompatible = false; + if (startBox.IsOutput) { var parameters = memberInfo.GetParameters(); ScriptType outType = startBox.CurrentType; + if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) + isCompatible = true; + if (!memberInfo.IsStatic) { var scriptType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); From 155d11c07b2427f7ac6e074244a849dd0fb7060c Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Wed, 27 Sep 2023 19:28:06 +0200 Subject: [PATCH 09/23] - Filtering now also applies when dragging a connection from an input port --- .../Surface/ContextMenu/VisjectCMItem.cs | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 370c5014a..be69d675c 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -156,32 +156,37 @@ namespace FlaxEditor.Surface.ContextMenu if(memberInfo.IsEvent) isCompatible = false; - if (startBox.IsOutput) - { - var parameters = memberInfo.GetParameters(); - ScriptType outType = startBox.CurrentType; - - if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) - isCompatible = true; - - if (!memberInfo.IsStatic) - { - var scriptType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); - isCompatible |= CanCastToType(scriptType, outType, _archetype.ConnectionsHints); - } - - if (!memberInfo.IsEvent) - { - for (int i = 0; i < parameters.Length; i++) - { - ScriptType inType = parameters[i].Type; - isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); - } - } - } + if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) + isCompatible = true; else { + if (startBox.IsOutput) + { + var parameters = memberInfo.GetParameters(); + ScriptType outType = startBox.CurrentType; + if (!memberInfo.IsStatic) + { + var scriptType = memberInfo.DeclaringType; + isCompatible |= CanCastToType(scriptType, outType, _archetype.ConnectionsHints); + } + + if (!memberInfo.IsEvent) + { + for (int i = 0; i < parameters.Length; i++) + { + ScriptType inType = parameters[i].Type; + isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); + } + } + } + else + { + ScriptType inType = startBox.CurrentType; + ScriptType outType = memberInfo.ValueType; + + isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); + } } } } From 9acee407469f45c474dcfee0eb7a8d3a38489326 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 28 Sep 2023 20:53:47 +0200 Subject: [PATCH 10/23] - Added context sensitive toggle gui --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 04130fc1e..0d4a35e51 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -40,6 +40,8 @@ namespace FlaxEditor.Surface.ContextMenu public delegate List ParameterGetterDelegate(); private readonly List _groups = new List(16); + private CheckBox _contextSensitiveToggle; + private bool _contextSensitiveSearchEnabled = true; private readonly TextBox _searchBox; private bool _waitingForInput; private VisjectCMGroup _surfaceParametersGroup; @@ -127,7 +129,7 @@ namespace FlaxEditor.Surface.ContextMenu _parameterSetNodeArchetype = info.ParameterSetNodeArchetype ?? Archetypes.Parameters.Nodes[3]; // Context menu dimensions - Size = new Float2(320, 248); + Size = new Float2(300, 400); var headerPanel = new Panel(ScrollBars.None) { @@ -139,17 +141,40 @@ namespace FlaxEditor.Surface.ContextMenu }; // Title bar + var titleFontReference = new FontReference(Style.Current.FontLarge.Asset, 10); var titleLabel = new Label { - Width = Width - 8, + Width = Width * 0.5f - 8f, Height = 20, X = 4, Parent = headerPanel, Text = "Select Node", - HorizontalAlignment = TextAlignment.Center, - Font = new FontReference(Style.Current.FontLarge.Asset, 10), + HorizontalAlignment = TextAlignment.Near, + Font = titleFontReference, }; + // Context sensitive toggle + var contextSensitiveLabel = new Label + { + Width = Width * 0.5f - 28, + Height = 20, + X = Width * 0.5f, + Parent = headerPanel, + Text = "Context Sensitive", + HorizontalAlignment = TextAlignment.Far, + Font = titleFontReference, + }; + + _contextSensitiveToggle = new CheckBox + { + Width = 20, + Height = 20, + X = Width - 24, + Parent = headerPanel, + Checked = _contextSensitiveSearchEnabled, + }; + _contextSensitiveToggle.StateChanged += OnContextSensitiveToggleStateChanged; + // Search box _searchBox = new SearchBox(false, 2, 22) { @@ -251,6 +276,11 @@ namespace FlaxEditor.Surface.ContextMenu } } + private void OnContextSensitiveToggleStateChanged(CheckBox checkBox) + { + _contextSensitiveSearchEnabled = checkBox.Checked; + } + /// /// Adds the group archetype to add to the menu. /// @@ -768,5 +798,12 @@ namespace FlaxEditor.Surface.ContextMenu { return GetPreviousSiblings(item).OfType(); } + + /// + public override void OnDestroy() + { + _contextSensitiveToggle.StateChanged -= OnContextSensitiveToggleStateChanged; + base.OnDestroy(); + } } } From 84b240216f38b0184851b0ddda23d1ca274fa9e4 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Thu, 28 Sep 2023 21:31:58 +0200 Subject: [PATCH 11/23] - Implemented context sensitive toggle functionality - Item list now updates on the fly when toggleing context sensitivity - Added profiling - Fixed a highlighting bug - Minor cleanup --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 40 ++++++++++++++----- .../Surface/ContextMenu/VisjectCMGroup.cs | 7 ++++ .../Surface/ContextMenu/VisjectCMItem.cs | 5 ++- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 0d4a35e51..67148a5a4 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -161,6 +161,7 @@ namespace FlaxEditor.Surface.ContextMenu X = Width * 0.5f, Parent = headerPanel, Text = "Context Sensitive", + TooltipText = "Should the nodes be filtered to only show those that can be connected in the current context?", HorizontalAlignment = TextAlignment.Far, Font = titleFontReference, }; @@ -276,11 +277,6 @@ namespace FlaxEditor.Surface.ContextMenu } } - private void OnContextSensitiveToggleStateChanged(CheckBox checkBox) - { - _contextSensitiveSearchEnabled = checkBox.Checked; - } - /// /// Adds the group archetype to add to the menu. /// @@ -318,7 +314,7 @@ namespace FlaxEditor.Surface.ContextMenu OnSearchFilterChanged(); } } - else + else if(_contextSensitiveSearchEnabled) { group.EvaluateVisibilityWithBox(_selectedBox); } @@ -355,7 +351,8 @@ namespace FlaxEditor.Surface.ContextMenu Parent = group }; } - group.EvaluateVisibilityWithBox(_selectedBox); + if(_contextSensitiveSearchEnabled) + group.EvaluateVisibilityWithBox(_selectedBox); group.SortChildren(); group.Parent = _groupsPanel; _groups.Add(group); @@ -465,7 +462,7 @@ namespace FlaxEditor.Surface.ContextMenu LockChildrenRecursive(); for (int i = 0; i < _groups.Count; i++) { - _groups[i].UpdateFilter(_searchBox.Text, _selectedBox); + _groups[i].UpdateFilter(_searchBox.Text, _contextSensitiveSearchEnabled ? _selectedBox : null); _groups[i].UpdateItemSort(_selectedBox); } SortGroups(); @@ -484,6 +481,30 @@ namespace FlaxEditor.Surface.ContextMenu Profiler.EndEvent(); } + private void OnContextSensitiveToggleStateChanged(CheckBox checkBox) + { + Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged"); + _contextSensitiveSearchEnabled = checkBox.Checked; + + LockChildrenRecursive(); + for (int i = 0; i < _groups.Count; i++) + { + _groups[i].UpdateFilter(_searchBox.Text, _contextSensitiveSearchEnabled ? _selectedBox : null); + } + SortGroups(); + UnlockChildrenRecursive(); + + Profiler.BeginEvent("VisjectCM.Layout"); + if (SelectedItem == null || !SelectedItem.VisibleInHierarchy) + SelectedItem = _groups.Find(g => g.Visible)?.Children.Find(c => c.Visible && c is VisjectCMItem) as VisjectCMItem; + PerformLayout(); + if (SelectedItem != null) + _panel1.ScrollViewTo(SelectedItem); + Profiler.EndEvent(); + + Profiler.EndEvent(); + } + /// /// Sort the groups and keeps in sync /// @@ -540,7 +561,8 @@ namespace FlaxEditor.Surface.ContextMenu for (int i = 0; i < _groups.Count; i++) { _groups[i].ResetView(); - _groups[i].EvaluateVisibilityWithBox(_selectedBox); + if(_contextSensitiveSearchEnabled) + _groups[i].EvaluateVisibilityWithBox(_selectedBox); } UnlockChildrenRecursive(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index 9560aecbb..d11d51223 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -132,6 +132,13 @@ namespace FlaxEditor.Surface.ContextMenu { if (selectedBox == null) { + for (int i = 0; i < _children.Count; i++) + { + if (_children[i] is VisjectCMItem item) + { + item.CanConnectTo(null); + } + } Visible = true; return; } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index be69d675c..4aa5c6ce8 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -309,11 +309,14 @@ namespace FlaxEditor.Surface.ContextMenu if (selectedBox != null) { if (!CanConnectTo(selectedBox)) + { + _highlights?.Clear(); return; + } } _isStartsWithMatch = _isFullMatch = false; - if (filterText == null) + if (string.IsNullOrEmpty(filterText)) { // Clear filter _highlights?.Clear(); From a4970b7fced7f1b6b81849b8527f6118a7665941 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 29 Sep 2023 16:25:34 +0200 Subject: [PATCH 12/23] - Group names now get filtered again (currently doing a second filtering pass through the items) - More cleanup --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 45 ++++++++----------- .../Surface/ContextMenu/VisjectCMGroup.cs | 8 ++-- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 67148a5a4..9955f0434 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -450,7 +450,25 @@ namespace FlaxEditor.Surface.ContextMenu return; Profiler.BeginEvent("VisjectCM.OnSearchFilterChanged"); + UpdateFilters(); + _searchBox.Focus(); + Profiler.EndEvent(); + } + + private void OnContextSensitiveToggleStateChanged(CheckBox checkBox) + { + // Skip events during setup or init stuff + if (IsLayoutLocked) + return; + Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged"); + _contextSensitiveSearchEnabled = checkBox.Checked; + UpdateFilters(); + Profiler.EndEvent(); + } + + private void UpdateFilters() + { if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null) { ResetView(); @@ -475,33 +493,6 @@ namespace FlaxEditor.Surface.ContextMenu PerformLayout(); if (SelectedItem != null) _panel1.ScrollViewTo(SelectedItem); - _searchBox.Focus(); - Profiler.EndEvent(); - - Profiler.EndEvent(); - } - - private void OnContextSensitiveToggleStateChanged(CheckBox checkBox) - { - Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged"); - _contextSensitiveSearchEnabled = checkBox.Checked; - - LockChildrenRecursive(); - for (int i = 0; i < _groups.Count; i++) - { - _groups[i].UpdateFilter(_searchBox.Text, _contextSensitiveSearchEnabled ? _selectedBox : null); - } - SortGroups(); - UnlockChildrenRecursive(); - - Profiler.BeginEvent("VisjectCM.Layout"); - if (SelectedItem == null || !SelectedItem.VisibleInHierarchy) - SelectedItem = _groups.Find(g => g.Visible)?.Children.Find(c => c.Visible && c is VisjectCMItem) as VisjectCMItem; - PerformLayout(); - if (SelectedItem != null) - _panel1.ScrollViewTo(SelectedItem); - Profiler.EndEvent(); - Profiler.EndEvent(); } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index d11d51223..efb8ab458 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -100,17 +100,17 @@ namespace FlaxEditor.Surface.ContextMenu } // Update header title - /*if (QueryFilterHelper.Match(filterText, HeaderText)) + if (QueryFilterHelper.Match(filterText, HeaderText)) { for (int i = 0; i < _children.Count; i++) { if (_children[i] is VisjectCMItem item) { - item.Visible = true; + item.UpdateFilter(null, selectedBox); + isAnyVisible |= item.Visible; } } - isAnyVisible = true; - }*/ + } // Update itself if (isAnyVisible) From 4bf46c3af3a353ee45635c30f70054373f680bd8 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 29 Sep 2023 17:19:38 +0200 Subject: [PATCH 13/23] - More cleanup and comments --- .../Editor/Surface/ContextMenu/VisjectCM.cs | 1 - .../Surface/ContextMenu/VisjectCMItem.cs | 106 +++++------------- Source/Editor/Surface/Elements/Box.cs | 54 +-------- .../Surface/VisjectSurface.Connecting.cs | 64 +++++++++++ 4 files changed, 93 insertions(+), 132 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 9955f0434..c07173aec 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; -using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 4aa5c6ce8..b0ff74272 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -80,7 +80,7 @@ namespace FlaxEditor.Surface.ContextMenu if (!Visible) return; - if (selectedBox != null && CanConnectTo(selectedBox, NodeArchetype)) + if (selectedBox != null && CanConnectTo(selectedBox)) SortScore += 1; if (Data != null) SortScore += 1; @@ -95,29 +95,14 @@ namespace FlaxEditor.Surface.ContextMenu textRect = new Rectangle(22, 0, Width - 24, Height); } - private bool CanConnectTo(Box startBox, NodeArchetype nodeArchetype) - { - if (startBox == null) - return false; - if (!startBox.IsOutput) - return false; // For now, I'm only handing the output box case - - if (nodeArchetype.Elements != null) - { - for (int i = 0; i < nodeArchetype.Elements.Length; i++) - { - if (nodeArchetype.Elements[i].Type == NodeElementType.Input && - startBox.CanUseType(nodeArchetype.Elements[i].ConnectionsType)) - { - return true; - } - } - } - return false; - } - + /// + /// Checks if this context menu item can be connected to a given box, before a node is actually spawned. + /// + /// The connected box + /// True if the connected box is compatible with this item public bool CanConnectTo(Box startBox) { + // Is compatible if box is null for reset reasons if (startBox == null) { Visible = true; @@ -131,15 +116,20 @@ namespace FlaxEditor.Surface.ContextMenu } bool isCompatible = false; + + // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items if (_archetype.Elements != null) { isCompatible = CheckElementsCompatibility(startBox); } + // Check compatibility based on the archetype tag or name. This handles custom groups and items, mainly function nodes for visual scripting if (_archetype.NodeTypeHint == NodeTypeHint.FunctionNode) { ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; + // Check if the archetype tag already has a member info otherwise try to fetch it via the archetype type and name + // Only really the InvokeMethod Nodes have a member info in their tag if (_archetype.Tag is ScriptMemberInfo info) { memberInfo = info; @@ -155,22 +145,29 @@ namespace FlaxEditor.Surface.ContextMenu { if(memberInfo.IsEvent) isCompatible = false; - + + // Box was dragged from an impulse port and the member info can be invoked so it is compatible if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) - isCompatible = true; + { + isCompatible = true; + } else { + // When the startBox is output we only need to check the input parameters if (startBox.IsOutput) { var parameters = memberInfo.GetParameters(); ScriptType outType = startBox.CurrentType; - + + // non static members have an instance input parameter if (!memberInfo.IsStatic) { var scriptType = memberInfo.DeclaringType; isCompatible |= CanCastToType(scriptType, outType, _archetype.ConnectionsHints); } + // We ignore event members here since they only have output parameters, which are currently not declared as such + // TODO: Fix bug where event member parameters 'IsOut' is set to false and not true if (!memberInfo.IsEvent) { for (int i = 0; i < parameters.Length; i++) @@ -182,6 +179,7 @@ namespace FlaxEditor.Surface.ContextMenu } else { + // When the startBox is input we only have to check the output type of the method ScriptType inType = startBox.CurrentType; ScriptType outType = memberInfo.ValueType; @@ -232,60 +230,10 @@ namespace FlaxEditor.Surface.ContextMenu { if (VisjectSurface.CanUseDirectCastStatic(type, currentType, false)) return true; - - var connectionsHints = hint; - if (currentType == ScriptType.Null && connectionsHints != ConnectionsHint.None) - { - if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything) - return true; - if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void)) - return true; - if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum) - return true; - if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray) - return true; - if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary) - return true; - if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector) - { - var t = type.Type; - if (t == typeof(Vector2) || - t == typeof(Vector3) || - t == typeof(Vector4) || - t == typeof(Float2) || - t == typeof(Float3) || - t == typeof(Float4) || - t == typeof(Double2) || - t == typeof(Double3) || - t == typeof(Double4) || - t == typeof(Int2) || - t == typeof(Int3) || - t == typeof(Int4) || - t == typeof(Color)) - { - return true; - } - } - if ((connectionsHints & ConnectionsHint.Scalar) == ConnectionsHint.Scalar) - { - var t = type.Type; - if (t == typeof(bool) || - t == typeof(char) || - t == typeof(byte) || - t == typeof(short) || - t == typeof(ushort) || - t == typeof(int) || - t == typeof(uint) || - t == typeof(long) || - t == typeof(ulong) || - t == typeof(float) || - t == typeof(double)) - { - return true; - } - } - } - + + if(VisjectSurface.IsTypeCompatible(currentType, type, hint)) + return true; + return CanCast(type, currentType); } diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index e771fa71c..5032b6048 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -231,58 +231,8 @@ namespace FlaxEditor.Surface.Elements } // Check using connection hints - var connectionsHints = ParentNode.Archetype.ConnectionsHints; - if (Archetype.ConnectionsType == ScriptType.Null && connectionsHints != ConnectionsHint.None) - { - if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything) - return true; - if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void)) - return true; - if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum) - return true; - if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray) - return true; - if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary) - return true; - if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector) - { - var t = type.Type; - if (t == typeof(Vector2) || - t == typeof(Vector3) || - t == typeof(Vector4) || - t == typeof(Float2) || - t == typeof(Float3) || - t == typeof(Float4) || - t == typeof(Double2) || - t == typeof(Double3) || - t == typeof(Double4) || - t == typeof(Int2) || - t == typeof(Int3) || - t == typeof(Int4) || - t == typeof(Color)) - { - return true; - } - } - if ((connectionsHints & ConnectionsHint.Scalar) == ConnectionsHint.Scalar) - { - var t = type.Type; - if (t == typeof(bool) || - t == typeof(char) || - t == typeof(byte) || - t == typeof(short) || - t == typeof(ushort) || - t == typeof(int) || - t == typeof(uint) || - t == typeof(long) || - t == typeof(ulong) || - t == typeof(float) || - t == typeof(double)) - { - return true; - } - } - } + if(VisjectSurface.IsTypeCompatible(Archetype.ConnectionsType, type, ParentNode.Archetype.ConnectionsHints)) + return true; // Check independent and if there is box with bigger potential because it may block current one from changing type var parentArch = ParentNode.Archetype; diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 2fbff7cb8..449f88f8c 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -136,6 +136,70 @@ namespace FlaxEditor.Surface return result; } + /// + /// Checks if a type is compatible with another type and can be casted by using a connection hint + /// + /// Source type + /// Type to check compatibility with + /// Hint to check if casting is possible + /// True if the source type is compatible with the target type + public static bool IsTypeCompatible(ScriptType from, ScriptType to, ConnectionsHint hint) + { + if (from == ScriptType.Null && hint != ConnectionsHint.None) + { + if ((hint & ConnectionsHint.Anything) == ConnectionsHint.Anything) + return true; + if ((hint & ConnectionsHint.Value) == ConnectionsHint.Value && to.Type != typeof(void)) + return true; + if ((hint & ConnectionsHint.Enum) == ConnectionsHint.Enum && to.IsEnum) + return true; + if ((hint & ConnectionsHint.Array) == ConnectionsHint.Array && to.IsArray) + return true; + if ((hint & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && to.IsDictionary) + return true; + if ((hint & ConnectionsHint.Vector) == ConnectionsHint.Vector) + { + var t = to.Type; + if (t == typeof(Vector2) || + t == typeof(Vector3) || + t == typeof(Vector4) || + t == typeof(Float2) || + t == typeof(Float3) || + t == typeof(Float4) || + t == typeof(Double2) || + t == typeof(Double3) || + t == typeof(Double4) || + t == typeof(Int2) || + t == typeof(Int3) || + t == typeof(Int4) || + t == typeof(Color)) + { + return true; + } + } + if ((hint & ConnectionsHint.Scalar) == ConnectionsHint.Scalar) + { + var t = to.Type; + if (t == typeof(bool) || + t == typeof(char) || + t == typeof(byte) || + t == typeof(short) || + t == typeof(ushort) || + t == typeof(int) || + t == typeof(uint) || + t == typeof(long) || + t == typeof(ulong) || + t == typeof(float) || + t == typeof(double)) + { + return true; + } + } + } + + return false; + } + /// /// Checks if can use direct conversion from one type to another. /// From 091d34b20d22a9cf6bbde017c8dc84064203f498 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 29 Sep 2023 17:26:56 +0200 Subject: [PATCH 14/23] - Even more cleanup and comments --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 7 +++---- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 2 +- Source/Editor/Surface/NodeArchetype.cs | 3 +++ Source/Editor/Surface/VisualScriptSurface.cs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index c07173aec..36f71a1a7 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -494,7 +494,7 @@ namespace FlaxEditor.Surface.ContextMenu _panel1.ScrollViewTo(SelectedItem); Profiler.EndEvent(); } - + /// /// Sort the groups and keeps in sync /// @@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.ContextMenu SortGroups(); PerformLayout(); - + Profiler.EndEvent(); } @@ -676,11 +676,10 @@ namespace FlaxEditor.Surface.ContextMenu // Prepare UpdateSurfaceParametersGroup(); ResetView(); - _panel1.VScrollBar.TargetValue = 0; Focus(); _waitingForInput = true; - + base.OnShow(); } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index b0ff74272..950303800 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -336,7 +336,7 @@ namespace FlaxEditor.Surface.ContextMenu } } } - + /// public override void Draw() { diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index 1cdaee2d2..4255b7199 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -73,6 +73,9 @@ namespace FlaxEditor.Surface All = Scalar | Vector | Enum | Anything | Value | Array | Dictionary, } + /// + /// Node type hint. Helps to distinguish special archetypes + /// [HideInEditor] public enum NodeTypeHint { diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index b6c0112a9..04c4da9d2 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -370,7 +370,7 @@ namespace FlaxEditor.Surface bindNode.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); bindNode.SubTitle = string.Format(" (in {0})", scriptTypeName); ((IList)group.Archetypes).Add(bindNode); - + // Add Unbind event node var unbindNode = (NodeArchetype)Archetypes.Function.Nodes[9].Clone(); unbindNode.DefaultValues[0] = scriptTypeTypeName; From b5dc91656819b072c82d0080c3e44dffd9f2938e Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 29 Sep 2023 17:44:31 +0200 Subject: [PATCH 15/23] - Moved ScriptType casting check from Box to ScriptType class - Even more cleanup and comments, wowzers --- Source/Editor/Scripting/ScriptType.cs | 27 ++++++++ .../Surface/ContextMenu/VisjectCMItem.cs | 65 ++++++++----------- Source/Editor/Surface/Elements/Box.cs | 21 ++---- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 454c3a5d2..e3de348d2 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -1393,5 +1393,32 @@ namespace FlaxEditor.Scripting return _custom.GetMembers(name, MemberTypes.Method, bindingAttr).FirstOrDefault(); return ScriptMemberInfo.Null; } + + /// + /// Basic check to see if a type could be casted to another type + /// + /// Source type + /// Target type + /// True if the type can be casted + public static bool CanCast(ScriptType from, ScriptType to) + { + if (from == to) + return true; + if (from == Null || to == Null) + return false; + return (from.Type != typeof(void) && from.Type != typeof(FlaxEngine.Object)) && + (to.Type != typeof(void) && to.Type != typeof(FlaxEngine.Object)) && + from.IsAssignableFrom(to); + } + + /// + /// Basic check to see if this type could be casted to another type + /// + /// Target type + /// True if the type can be casted + public bool CanCastTo(ScriptType to) + { + return CanCast(this, to); + } } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 950303800..e3f4b0e66 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEditor.Utilities; @@ -117,12 +116,6 @@ namespace FlaxEditor.Surface.ContextMenu bool isCompatible = false; - // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items - if (_archetype.Elements != null) - { - isCompatible = CheckElementsCompatibility(startBox); - } - // Check compatibility based on the archetype tag or name. This handles custom groups and items, mainly function nodes for visual scripting if (_archetype.NodeTypeHint == NodeTypeHint.FunctionNode) { @@ -134,18 +127,18 @@ namespace FlaxEditor.Surface.ContextMenu { memberInfo = info; } - else if(_archetype.DefaultValues is { Length: > 1 }) + else if(_archetype.DefaultValues is { Length: > 1 }) // We have to check since VisualScriptFunctionNode and ReturnNode don't have a name and type { var eventName = (string)_archetype.DefaultValues[1]; var eventType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); - memberInfo = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + memberInfo = eventType.GetMember(eventName, System.Reflection.MemberTypes.Event, + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.Instance); } if (memberInfo != ScriptMemberInfo.Null) { - if(memberInfo.IsEvent) - isCompatible = false; - // Box was dragged from an impulse port and the member info can be invoked so it is compatible if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) { @@ -163,7 +156,7 @@ namespace FlaxEditor.Surface.ContextMenu if (!memberInfo.IsStatic) { var scriptType = memberInfo.DeclaringType; - isCompatible |= CanCastToType(scriptType, outType, _archetype.ConnectionsHints); + isCompatible |= CanCastType(scriptType, outType, _archetype.ConnectionsHints); } // We ignore event members here since they only have output parameters, which are currently not declared as such @@ -173,7 +166,7 @@ namespace FlaxEditor.Surface.ContextMenu for (int i = 0; i < parameters.Length; i++) { ScriptType inType = parameters[i].Type; - isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); + isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); } } } @@ -183,11 +176,16 @@ namespace FlaxEditor.Surface.ContextMenu ScriptType inType = startBox.CurrentType; ScriptType outType = memberInfo.ValueType; - isCompatible |= CanCastToType(inType, outType, _archetype.ConnectionsHints); + isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); } } } } + else if (_archetype.Elements != null) + { + // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items + isCompatible = CheckElementsCompatibility(startBox); + } Visible = isCompatible; return isCompatible; @@ -198,54 +196,47 @@ namespace FlaxEditor.Surface.ContextMenu bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) { + // Ignore all elements that aren't inputs or outputs (e.g. input fields) if(element.Type != NodeElementType.Output && element.Type != NodeElementType.Input) continue; + // Ignore elements with the same direction as the box if ((startBox.IsOutput && element.Type == NodeElementType.Output) || (!startBox.IsOutput && element.Type == NodeElementType.Input)) continue; - ScriptType inType; - ScriptType outType; + ScriptType fromType; + ScriptType toType; ConnectionsHint hint; if (startBox.IsOutput) { - inType = element.ConnectionsType; - outType = startBox.CurrentType; + fromType = element.ConnectionsType; + toType = startBox.CurrentType; hint = _archetype.ConnectionsHints; } else { - inType = startBox.CurrentType; - outType = element.ConnectionsType; + fromType = startBox.CurrentType; + toType = element.ConnectionsType; hint = startBox.ParentNode.Archetype.ConnectionsHints; } - isCompatible |= CanCastToType(inType, outType, hint); + isCompatible |= CanCastType(fromType, toType, hint); } return isCompatible; } - private bool CanCastToType(ScriptType currentType, ScriptType type, ConnectionsHint hint) + private bool CanCastType(ScriptType from, ScriptType to, ConnectionsHint hint) { - if (VisjectSurface.CanUseDirectCastStatic(type, currentType, false)) + // Yes, from and to are switched on purpose + if (VisjectSurface.CanUseDirectCastStatic(to, from, false)) return true; - if(VisjectSurface.IsTypeCompatible(currentType, type, hint)) + if(VisjectSurface.IsTypeCompatible(from, to, hint)) return true; - return CanCast(type, currentType); - } - - private static bool CanCast(ScriptType oB, ScriptType iB) - { - if (oB == iB) - return true; - if (oB == ScriptType.Null || iB == ScriptType.Null) - return false; - return (oB.Type != typeof(void) && oB.Type != typeof(FlaxEngine.Object)) && - (iB.Type != typeof(void) && iB.Type != typeof(FlaxEngine.Object)) && - oB.IsAssignableFrom(iB); + // Same here + return to.CanCastTo(from); } /// diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 5032b6048..6a265a21e 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -91,7 +91,7 @@ namespace FlaxEditor.Surface.Elements _currentType = value; // Check if will need to update box connections due to type change - if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !CanCast(prev, _currentType)) + if ((Surface == null || Surface._isUpdatingBoxTypes == 0) && HasAnyConnection && !prev.CanCastTo(_currentType)) { // Remove all invalid connections and update those which still can be valid var connections = Connections.ToArray(); @@ -246,7 +246,7 @@ namespace FlaxEditor.Surface.Elements var b = ParentNode.GetBox(boxes[i]); // Check if its the same and tested type matches the default value type - if (b == this && CanCast(parentArch.DefaultType, type)) + if (b == this && parentArch.DefaultType.CanCastTo(type)) { // Can return true; @@ -668,17 +668,6 @@ namespace FlaxEditor.Surface.Elements } } - private static bool CanCast(ScriptType oB, ScriptType iB) - { - if (oB == iB) - return true; - if (oB == ScriptType.Null || iB == ScriptType.Null) - return false; - return (oB.Type != typeof(void) && oB.Type != typeof(FlaxEngine.Object)) && - (iB.Type != typeof(void) && iB.Type != typeof(FlaxEngine.Object)) && - oB.IsAssignableFrom(iB); - } - /// public bool AreConnected(IConnectionInstigator other) { @@ -737,7 +726,7 @@ namespace FlaxEditor.Surface.Elements { if (!iB.CanUseType(oB.CurrentType)) { - if (!CanCast(oB.CurrentType, iB.CurrentType)) + if (!oB.CurrentType.CanCastTo(iB.CurrentType)) { // Cannot return false; @@ -748,7 +737,7 @@ namespace FlaxEditor.Surface.Elements { if (!oB.CanUseType(iB.CurrentType)) { - if (!CanCast(oB.CurrentType, iB.CurrentType)) + if (!oB.CurrentType.CanCastTo(iB.CurrentType)) { // Cannot return false; @@ -821,7 +810,7 @@ namespace FlaxEditor.Surface.Elements bool useCaster = false; if (!iB.CanUseType(oB.CurrentType)) { - if (CanCast(oB.CurrentType, iB.CurrentType)) + if (oB.CurrentType.CanCastTo(iB.CurrentType)) useCaster = true; else return; From 3befe4bb4a036b7fff278bffb7d90de88baf4194 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Fri, 29 Sep 2023 21:53:34 +0200 Subject: [PATCH 16/23] - Fixed a bug where items with a string match didn't get highlighted anymore when the group name matched - Remouved double filtering when group name matched - Started fixing asynchronous Un/Packing nodes filtering --- Source/Editor/Surface/Archetypes/Packing.cs | 2 + .../Surface/ContextMenu/VisjectCMGroup.cs | 21 +--- .../Surface/ContextMenu/VisjectCMItem.cs | 109 +++++++++--------- Source/Editor/Surface/NodeArchetype.cs | 5 + 4 files changed, 67 insertions(+), 70 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 5a5e9cd5b..54524dc13 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -353,6 +353,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch), Description = "Makes the structure data to from the components.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.PackingNode, Size = new Float2(180, 20), DefaultValues = new object[] { @@ -463,6 +464,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch), Description = "Breaks the structure data to allow extracting components from it.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, + NodeTypeHint = NodeTypeHint.PackingNode, Size = new Float2(180, 20), DefaultValues = new object[] { diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index efb8ab458..9ab67f856 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -90,28 +90,16 @@ namespace FlaxEditor.Surface.ContextMenu // Update items bool isAnyVisible = false; + bool groupHeaderMatches = QueryFilterHelper.Match(filterText, HeaderText); for (int i = 0; i < _children.Count; i++) { if (_children[i] is VisjectCMItem item) { - item.UpdateFilter(filterText, selectedBox); + item.UpdateFilter(filterText, selectedBox, groupHeaderMatches); isAnyVisible |= item.Visible; } } - // Update header title - if (QueryFilterHelper.Match(filterText, HeaderText)) - { - for (int i = 0; i < _children.Count; i++) - { - if (_children[i] is VisjectCMItem item) - { - item.UpdateFilter(null, selectedBox); - isAnyVisible |= item.Visible; - } - } - } - // Update itself if (isAnyVisible) { @@ -136,7 +124,7 @@ namespace FlaxEditor.Surface.ContextMenu { if (_children[i] is VisjectCMItem item) { - item.CanConnectTo(null); + item.Visible = true; } } Visible = true; @@ -150,7 +138,8 @@ namespace FlaxEditor.Surface.ContextMenu { if (_children[i] is VisjectCMItem item) { - isAnyVisible |= item.CanConnectTo(selectedBox); + item.Visible = item.CanConnectTo(selectedBox); + isAnyVisible |= item.Visible; } } diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index e3f4b0e66..65ba19909 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -103,29 +103,23 @@ namespace FlaxEditor.Surface.ContextMenu { // Is compatible if box is null for reset reasons if (startBox == null) - { - Visible = true; return true; - } if (_archetype == null) - { - Visible = false; return false; - } bool isCompatible = false; // Check compatibility based on the archetype tag or name. This handles custom groups and items, mainly function nodes for visual scripting - if (_archetype.NodeTypeHint == NodeTypeHint.FunctionNode) + if (_archetype.NodeTypeHint is NodeTypeHint.FunctionNode) { ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; // Check if the archetype tag already has a member info otherwise try to fetch it via the archetype type and name // Only really the InvokeMethod Nodes have a member info in their tag - if (_archetype.Tag is ScriptMemberInfo info) + if (_archetype.Tag is ScriptMemberInfo tagInfo) { - memberInfo = info; + memberInfo = tagInfo; } else if(_archetype.DefaultValues is { Length: > 1 }) // We have to check since VisualScriptFunctionNode and ReturnNode don't have a name and type { @@ -139,46 +133,7 @@ namespace FlaxEditor.Surface.ContextMenu if (memberInfo != ScriptMemberInfo.Null) { - // Box was dragged from an impulse port and the member info can be invoked so it is compatible - if (startBox.CurrentType.IsVoid && memberInfo.ValueType.IsVoid) - { - isCompatible = true; - } - else - { - // When the startBox is output we only need to check the input parameters - if (startBox.IsOutput) - { - var parameters = memberInfo.GetParameters(); - ScriptType outType = startBox.CurrentType; - - // non static members have an instance input parameter - if (!memberInfo.IsStatic) - { - var scriptType = memberInfo.DeclaringType; - isCompatible |= CanCastType(scriptType, outType, _archetype.ConnectionsHints); - } - - // We ignore event members here since they only have output parameters, which are currently not declared as such - // TODO: Fix bug where event member parameters 'IsOut' is set to false and not true - if (!memberInfo.IsEvent) - { - for (int i = 0; i < parameters.Length; i++) - { - ScriptType inType = parameters[i].Type; - isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); - } - } - } - else - { - // When the startBox is input we only have to check the output type of the method - ScriptType inType = startBox.CurrentType; - ScriptType outType = memberInfo.ValueType; - - isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); - } - } + isCompatible |= CheckMemberInfoCompatibility(startBox, memberInfo); } } else if (_archetype.Elements != null) @@ -186,11 +141,56 @@ namespace FlaxEditor.Surface.ContextMenu // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items isCompatible = CheckElementsCompatibility(startBox); } - - Visible = isCompatible; + return isCompatible; } + private bool CheckMemberInfoCompatibility(Box startBox, ScriptMemberInfo info) + { + bool isCompatible = false; + // Box was dragged from an impulse port and the member info can be invoked so it is compatible + if (startBox.CurrentType.IsVoid && info.ValueType.IsVoid) + { + isCompatible = true; + } + else + { + // When the startBox is output we only need to check the input parameters + if (startBox.IsOutput && !info.IsField) + { + var parameters = info.GetParameters(); + ScriptType outType = startBox.CurrentType; + + // non static members have an instance input parameter + if (!info.IsStatic) + { + var scriptType = info.DeclaringType; + isCompatible |= CanCastType(scriptType, outType, _archetype.ConnectionsHints); + } + + // We ignore event members here since they only have output parameters, which are currently not declared as such + // TODO: Fix bug where event member parameters 'IsOut' is set to false and not true + if (!info.IsEvent) + { + for (int i = 0; i < parameters.Length; i++) + { + ScriptType inType = parameters[i].Type; + isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); + } + } + } + else + { + // When the startBox is input we only have to check the output type of the method + ScriptType inType = startBox.CurrentType; + ScriptType outType = info.ValueType; + + isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); + } + } + return isCompatible; + } + private bool CheckElementsCompatibility(Box startBox) { bool isCompatible = false; @@ -243,11 +243,12 @@ namespace FlaxEditor.Surface.ContextMenu /// Updates the filter. /// /// The filter text. - public void UpdateFilter(string filterText, Box selectedBox) + public void UpdateFilter(string filterText, Box selectedBox, bool groupHeaderMatches = false) { if (selectedBox != null) { - if (!CanConnectTo(selectedBox)) + Visible = CanConnectTo(selectedBox); + if (!Visible) { _highlights?.Clear(); return; @@ -319,7 +320,7 @@ namespace FlaxEditor.Surface.ContextMenu Data = data; } - else + else if(!groupHeaderMatches) { // Hide _highlights?.Clear(); diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index 4255b7199..146618ae7 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -88,6 +88,11 @@ namespace FlaxEditor.Surface /// Is Function Node. /// FunctionNode = 1, + + /// + /// Is custom Packing Node. + /// + PackingNode = 2, } /// From 35f641955b0587a3adc61b7296f46f7dc209c1e6 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 13:20:04 +0200 Subject: [PATCH 17/23] - Removed NodeTypeHint - Added delegates to check compatiblity with custom archetypes - Added compatibility check to InvokeMethod archetype --- Source/Editor/Surface/Archetypes/Function.cs | 53 +++++++++++++++---- Source/Editor/Surface/Archetypes/Packing.cs | 2 - .../Surface/ContextMenu/VisjectCMItem.cs | 46 ++++++++-------- Source/Editor/Surface/NodeArchetype.cs | 47 +++++++--------- .../Surface/VisjectSurface.Connecting.cs | 11 ++++ 5 files changed, 92 insertions(+), 67 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index d027dd8f1..422ebf35b 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -773,7 +773,7 @@ namespace FlaxEditor.Surface.Archetypes Values[4] = GetSignatureData(memberInfo, memberInfo.GetParameters()); } } - + private SignatureInfo LoadSignature() { var signature = new SignatureInfo(); @@ -1151,6 +1151,45 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + if (nodeArch.Tag is not ScriptMemberInfo memberInfo) + return false; + if(memberInfo.ValueType.IsVoid && outputType.IsVoid) + return true; + if (!memberInfo.IsStatic) + { + if (VisjectSurface.FullCastCheck(memberInfo.DeclaringType, outputType, hint)) + return true; + } + foreach (var param in memberInfo.GetParameters()) + { + if(param.IsOut) + continue; + if (VisjectSurface.FullCastCheck(param.Type, outputType, hint)) + return true; + } + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + if (nodeArch.Tag is not ScriptMemberInfo memberInfo) + return false; + if(memberInfo.ValueType.IsVoid && inputType.IsVoid) + return true; + if (VisjectSurface.FullCastCheck(memberInfo.ValueType, inputType, hint)) + return true; + foreach (var param in memberInfo.GetParameters()) + { + if(!param.IsOut) + continue; + if (VisjectSurface.FullCastCheck(param.Type, inputType, hint)) + return true; + } + return false; + } } private sealed class ReturnNode : SurfaceNode @@ -2228,7 +2267,6 @@ namespace FlaxEditor.Surface.Archetypes Title = "Function Input", Description = "The graph function input data", Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaPaste, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2248,7 +2286,6 @@ namespace FlaxEditor.Surface.Archetypes Title = "Function Output", Description = "The graph function output data", Flags = NodeFlags.MaterialGraph | NodeFlags.ParticleEmitterGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaPaste, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2267,7 +2304,6 @@ namespace FlaxEditor.Surface.Archetypes Title = string.Empty, Description = "Overrides the base class method with custom implementation", Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2280,9 +2316,10 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Create = (id, context, arch, groupArch) => new InvokeMethodNode(id, context, arch, groupArch), + IsInputCompatible = InvokeMethodNode.IsInputCompatible, + IsOutputCompatible = InvokeMethodNode.IsOutputCompatible, Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2306,7 +2343,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new ReturnNode(id, context, arch, groupArch), Title = "Return", Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(100, 40), DefaultValues = new object[] { @@ -2325,7 +2361,6 @@ namespace FlaxEditor.Surface.Archetypes Title = "New Function", Description = "Adds a new function to the script", Flags = NodeFlags.VisualScriptGraph, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 20), DefaultValues = new object[] { @@ -2338,7 +2373,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new GetFieldNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2354,7 +2388,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new SetFieldNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(240, 60), DefaultValues = new object[] { @@ -2371,7 +2404,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(260, 60), DefaultValues = new object[] { @@ -2394,7 +2426,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch), Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.FunctionNode, Size = new Float2(260, 60), DefaultValues = new object[] { diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 54524dc13..5a5e9cd5b 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -353,7 +353,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch), Description = "Makes the structure data to from the components.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.PackingNode, Size = new Float2(180, 20), DefaultValues = new object[] { @@ -464,7 +463,6 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch), Description = "Breaks the structure data to allow extracting components from it.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, - NodeTypeHint = NodeTypeHint.PackingNode, Size = new Float2(180, 20), DefaultValues = new object[] { diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 65ba19909..1d645a94e 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -109,9 +109,23 @@ namespace FlaxEditor.Surface.ContextMenu return false; bool isCompatible = false; + + if (startBox.IsOutput && _archetype.IsInputCompatible != null) + { + isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints); + } + else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null) + { + isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints); + } + else if (_archetype.Elements != null) + { + // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items + isCompatible = CheckElementsCompatibility(startBox); + } // Check compatibility based on the archetype tag or name. This handles custom groups and items, mainly function nodes for visual scripting - if (_archetype.NodeTypeHint is NodeTypeHint.FunctionNode) + /*if (_archetype.NodeTypeHint is NodeTypeHint.FunctionNode) { ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; @@ -135,12 +149,8 @@ namespace FlaxEditor.Surface.ContextMenu { isCompatible |= CheckMemberInfoCompatibility(startBox, memberInfo); } - } - else if (_archetype.Elements != null) - { - // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items - isCompatible = CheckElementsCompatibility(startBox); - } + }*/ + /*else */ return isCompatible; } @@ -165,17 +175,16 @@ namespace FlaxEditor.Surface.ContextMenu if (!info.IsStatic) { var scriptType = info.DeclaringType; - isCompatible |= CanCastType(scriptType, outType, _archetype.ConnectionsHints); + isCompatible |= VisjectSurface.FullCastCheck(scriptType, outType, _archetype.ConnectionsHints); } // We ignore event members here since they only have output parameters, which are currently not declared as such - // TODO: Fix bug where event member parameters 'IsOut' is set to false and not true if (!info.IsEvent) { for (int i = 0; i < parameters.Length; i++) { ScriptType inType = parameters[i].Type; - isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); + isCompatible |= VisjectSurface.FullCastCheck(inType, outType, _archetype.ConnectionsHints); } } } @@ -185,7 +194,7 @@ namespace FlaxEditor.Surface.ContextMenu ScriptType inType = startBox.CurrentType; ScriptType outType = info.ValueType; - isCompatible |= CanCastType(inType, outType, _archetype.ConnectionsHints); + isCompatible |= VisjectSurface.FullCastCheck(inType, outType, _archetype.ConnectionsHints); } } return isCompatible; @@ -220,25 +229,12 @@ namespace FlaxEditor.Surface.ContextMenu hint = startBox.ParentNode.Archetype.ConnectionsHints; } - isCompatible |= CanCastType(fromType, toType, hint); + isCompatible |= VisjectSurface.FullCastCheck(fromType, toType, hint); } return isCompatible; } - private bool CanCastType(ScriptType from, ScriptType to, ConnectionsHint hint) - { - // Yes, from and to are switched on purpose - if (VisjectSurface.CanUseDirectCastStatic(to, from, false)) - return true; - - if(VisjectSurface.IsTypeCompatible(from, to, hint)) - return true; - - // Same here - return to.CanCastTo(from); - } - /// /// Updates the filter. /// diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index 146618ae7..1a7f88f20 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -72,28 +72,6 @@ namespace FlaxEditor.Surface /// All = Scalar | Vector | Enum | Anything | Value | Array | Dictionary, } - - /// - /// Node type hint. Helps to distinguish special archetypes - /// - [HideInEditor] - public enum NodeTypeHint - { - /// - /// Is Node. - /// - Default = 0, - - /// - /// Is Function Node. - /// - FunctionNode = 1, - - /// - /// Is custom Packing Node. - /// - PackingNode = 2, - } /// /// Surface node archetype description. @@ -111,6 +89,11 @@ namespace FlaxEditor.Surface /// The created node object. public delegate SurfaceNode CreateCustomNodeFunc(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch); + /// + /// Checks if the given type is compatible with the given node archetype. Used for custom nodes + /// + public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint); + /// /// Unique node type ID within a single group. /// @@ -121,6 +104,16 @@ namespace FlaxEditor.Surface /// public CreateCustomNodeFunc Create; + /// + /// Function for asynchronously loaded nodes to check if input ports are compatible, for filtering. + /// + public IsCompatible IsInputCompatible; + + /// + /// Function for asynchronously loaded nodes to check if output ports are compatible, for filtering. + /// + public IsCompatible IsOutputCompatible; + /// /// Default initial size of the node. /// @@ -130,7 +123,7 @@ namespace FlaxEditor.Surface /// Custom set of flags. /// public NodeFlags Flags; - + /// /// Title text. /// @@ -173,11 +166,6 @@ namespace FlaxEditor.Surface /// Connections hints. /// public ConnectionsHint ConnectionsHints; - - /// - /// Node Type hints. - /// - public NodeTypeHint NodeTypeHint; /// /// Array with independent boxes IDs. @@ -211,6 +199,8 @@ namespace FlaxEditor.Surface { TypeID = TypeID, Create = Create, + IsInputCompatible = IsInputCompatible, + IsOutputCompatible = IsOutputCompatible, Size = Size, Flags = Flags, Title = Title, @@ -220,7 +210,6 @@ namespace FlaxEditor.Surface DefaultValues = (object[])DefaultValues?.Clone(), DefaultType = DefaultType, ConnectionsHints = ConnectionsHints, - NodeTypeHint = NodeTypeHint, IndependentBoxes = (int[])IndependentBoxes?.Clone(), DependentBoxes = (int[])DependentBoxes?.Clone(), Elements = (NodeElementArchetype[])Elements?.Clone(), diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 449f88f8c..9ca4f82e8 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -199,6 +199,17 @@ namespace FlaxEditor.Surface return false; } + + public static bool FullCastCheck(ScriptType from, ScriptType to, ConnectionsHint hint) + { + // Yes, from and to are switched on purpose + if (CanUseDirectCastStatic(to, from, false)) + return true; + if(IsTypeCompatible(from, to, hint)) + return true; + // Same here + return to.CanCastTo(from); + } /// /// Checks if can use direct conversion from one type to another. From 719efc4a99d3f5d3d6ec8b98c2411c947fab7c3c Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 13:45:21 +0200 Subject: [PATCH 18/23] - Added Input/Output compatibility check to event based nodes - Removed a huge chunk of compatibility checking code out of CMItem --- Source/Editor/Surface/Archetypes/Function.cs | 41 ++++++++++ .../Surface/ContextMenu/VisjectCMItem.cs | 74 ------------------- 2 files changed, 41 insertions(+), 74 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 422ebf35b..975267ad2 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -2223,6 +2223,43 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + // Event based nodes always have a pulse input, so it's always compatible with void + if (outputType.IsVoid) + return true; + + var eventName = (string)nodeArch.DefaultValues[1]; + var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + if (member && SurfaceUtils.IsValidVisualScriptEvent(member)) + { + if (!member.IsStatic) + { + if (VisjectSurface.FullCastCheck(eventType, outputType, hint)) + return true; + } + } + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + // Event based nodes always have a pulse output, so it's always compatible with void + if (inputType.IsVoid) + return true; + + var eventName = (string)nodeArch.DefaultValues[1]; + var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + if (member && SurfaceUtils.IsValidVisualScriptEvent(member)) + { + if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint)) + return true; + } + return false; + } } private sealed class BindEventNode : EventBaseNode @@ -2402,6 +2439,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 9, Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch), + IsInputCompatible = EventBaseNode.IsInputCompatible, + IsOutputCompatible = EventBaseNode.IsOutputCompatible, Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(260, 60), @@ -2424,6 +2463,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 10, Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch), + IsInputCompatible = EventBaseNode.IsInputCompatible, + IsOutputCompatible = EventBaseNode.IsOutputCompatible, Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(260, 60), diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 1d645a94e..70c60f9bf 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -109,7 +109,6 @@ namespace FlaxEditor.Surface.ContextMenu return false; bool isCompatible = false; - if (startBox.IsOutput && _archetype.IsInputCompatible != null) { isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints); @@ -124,79 +123,6 @@ namespace FlaxEditor.Surface.ContextMenu isCompatible = CheckElementsCompatibility(startBox); } - // Check compatibility based on the archetype tag or name. This handles custom groups and items, mainly function nodes for visual scripting - /*if (_archetype.NodeTypeHint is NodeTypeHint.FunctionNode) - { - ScriptMemberInfo memberInfo = ScriptMemberInfo.Null; - - // Check if the archetype tag already has a member info otherwise try to fetch it via the archetype type and name - // Only really the InvokeMethod Nodes have a member info in their tag - if (_archetype.Tag is ScriptMemberInfo tagInfo) - { - memberInfo = tagInfo; - } - else if(_archetype.DefaultValues is { Length: > 1 }) // We have to check since VisualScriptFunctionNode and ReturnNode don't have a name and type - { - var eventName = (string)_archetype.DefaultValues[1]; - var eventType = TypeUtils.GetType((string)_archetype.DefaultValues[0]); - memberInfo = eventType.GetMember(eventName, System.Reflection.MemberTypes.Event, - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.Instance); - } - - if (memberInfo != ScriptMemberInfo.Null) - { - isCompatible |= CheckMemberInfoCompatibility(startBox, memberInfo); - } - }*/ - /*else */ - - return isCompatible; - } - - private bool CheckMemberInfoCompatibility(Box startBox, ScriptMemberInfo info) - { - bool isCompatible = false; - // Box was dragged from an impulse port and the member info can be invoked so it is compatible - if (startBox.CurrentType.IsVoid && info.ValueType.IsVoid) - { - isCompatible = true; - } - else - { - // When the startBox is output we only need to check the input parameters - if (startBox.IsOutput && !info.IsField) - { - var parameters = info.GetParameters(); - ScriptType outType = startBox.CurrentType; - - // non static members have an instance input parameter - if (!info.IsStatic) - { - var scriptType = info.DeclaringType; - isCompatible |= VisjectSurface.FullCastCheck(scriptType, outType, _archetype.ConnectionsHints); - } - - // We ignore event members here since they only have output parameters, which are currently not declared as such - if (!info.IsEvent) - { - for (int i = 0; i < parameters.Length; i++) - { - ScriptType inType = parameters[i].Type; - isCompatible |= VisjectSurface.FullCastCheck(inType, outType, _archetype.ConnectionsHints); - } - } - } - else - { - // When the startBox is input we only have to check the output type of the method - ScriptType inType = startBox.CurrentType; - ScriptType outType = info.ValueType; - - isCompatible |= VisjectSurface.FullCastCheck(inType, outType, _archetype.ConnectionsHints); - } - } return isCompatible; } From 1dc01cd0238142c3e4336a824f8b96f0e8c7b0d2 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 20:07:33 +0200 Subject: [PATCH 19/23] - Added compatibility checks to packing structures nodes - Added compatibility checks to more function nodes --- Source/Editor/Surface/Archetypes/Function.cs | 135 ++++++++++++++++++- Source/Editor/Surface/Archetypes/Packing.cs | 82 +++++++++++ 2 files changed, 212 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 975267ad2..cf680c06e 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1156,14 +1156,19 @@ namespace FlaxEditor.Surface.Archetypes { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; - if(memberInfo.ValueType.IsVoid && outputType.IsVoid) - return true; + if (!memberInfo.IsStatic) { if (VisjectSurface.FullCastCheck(memberInfo.DeclaringType, outputType, hint)) return true; } - foreach (var param in memberInfo.GetParameters()) + + var parameters = memberInfo.GetParameters(); + bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid); + if (outputType.IsVoid) + return !isPure; + + foreach (var param in parameters) { if(param.IsOut) continue; @@ -1177,10 +1182,14 @@ namespace FlaxEditor.Surface.Archetypes { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; - if(memberInfo.ValueType.IsVoid && inputType.IsVoid) - return true; if (VisjectSurface.FullCastCheck(memberInfo.ValueType, inputType, hint)) return true; + + var parameters = memberInfo.GetParameters(); + bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid); + if (inputType.IsVoid) + return !isPure; + foreach (var param in memberInfo.GetParameters()) { if(!param.IsOut) @@ -1816,6 +1825,16 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + return inputType.IsVoid; + } } private abstract class FieldNodeBase : SurfaceNode @@ -1952,6 +1971,64 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get " + SurfaceUtils.GetMethodDisplayName((string)Values[1]); UpdateSignature(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + if (scriptType == ScriptType.Null) + return false; + + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + foreach (var member in members) + { + if (!SurfaceUtils.IsValidVisualScriptField(member)) + continue; + + if (member) + { + if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint)) + return true; + } + else + { + var isStatic = (bool)nodeArch.DefaultValues[3]; + if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint)) + return true; + } + break; + } + + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + if (scriptType == ScriptType.Null) + return false; + + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + foreach (var member in members) + { + if (!SurfaceUtils.IsValidVisualScriptField(member)) + continue; + + if (member) + { + if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint)) + return true; + } + else + { + var typeName = (string)nodeArch.DefaultValues[2]; + if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), inputType, hint)) + return true; + } + break; + } + + return false; + } } private sealed class SetFieldNode : FieldNodeBase @@ -2005,6 +2082,48 @@ namespace FlaxEditor.Surface.Archetypes Title = "Set " + SurfaceUtils.GetMethodDisplayName((string)Values[1]); UpdateSignature(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + if(outputType.IsVoid) + return true; + + var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + if (scriptType == ScriptType.Null) + return false; + + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + foreach (var member in members) + { + if (!SurfaceUtils.IsValidVisualScriptField(member)) + continue; + + if (member) + { + if (VisjectSurface.FullCastCheck(member.ValueType, outputType, hint)) + return true; + if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint)) + return true; + } + else + { + var typeName = (string)nodeArch.DefaultValues[2]; + if (VisjectSurface.FullCastCheck(TypeUtils.GetType(typeName), outputType, hint)) + return true; + var isStatic = (bool)nodeArch.DefaultValues[3]; + if (!isStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint)) + return true; + } + break; + } + + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + return inputType.IsVoid; + } } private abstract class EventBaseNode : SurfaceNode, IFunctionsDependantNode @@ -2395,6 +2514,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 6, Create = (id, context, arch, groupArch) => new VisualScriptFunctionNode(id, context, arch, groupArch), + IsInputCompatible = VisualScriptFunctionNode.IsInputCompatible, + IsOutputCompatible = VisualScriptFunctionNode.IsOutputCompatible, Title = "New Function", Description = "Adds a new function to the script", Flags = NodeFlags.VisualScriptGraph, @@ -2408,6 +2529,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 7, Create = (id, context, arch, groupArch) => new GetFieldNode(id, context, arch, groupArch), + IsInputCompatible = GetFieldNode.IsInputCompatible, + IsOutputCompatible = GetFieldNode.IsOutputCompatible, Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(240, 60), @@ -2423,6 +2546,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 8, Create = (id, context, arch, groupArch) => new SetFieldNode(id, context, arch, groupArch), + IsInputCompatible = SetFieldNode.IsInputCompatible, + IsOutputCompatible = SetFieldNode.IsOutputCompatible, Title = string.Empty, Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(240, 60), diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 5a5e9cd5b..ccabaaa6d 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -207,6 +207,26 @@ namespace FlaxEditor.Surface.Archetypes AddElement(box); } } + + protected static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + // Event based nodes always have a pulse input, so it's always compatible with void + if (outputType.IsVoid) + return true; + + var eventName = (string)nodeArch.DefaultValues[1]; + var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); + var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + if (member && SurfaceUtils.IsValidVisualScriptEvent(member)) + { + if (!member.IsStatic) + { + if (VisjectSurface.FullCastCheck(eventType, outputType, hint)) + return true; + } + } + return false; + } } private sealed class PackStructureNode : StructureNode @@ -216,6 +236,35 @@ namespace FlaxEditor.Surface.Archetypes : base(id, context, nodeArch, groupArch, false) { } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + var typeName = (string)nodeArch.DefaultValues[0]; + var type = TypeUtils.GetType(typeName); + if (type) + { + var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray(); + var fieldsLength = fields.Length; + for (var i = 0; i < fieldsLength; i++) + { + if (VisjectSurface.FullCastCheck(fields[i].ValueType, outputType, hint)) + return true; + } + } + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + var typeName = (string)nodeArch.DefaultValues[0]; + var type = TypeUtils.GetType(typeName); + if (type) + { + if (VisjectSurface.FullCastCheck(type, inputType, hint)) + return true; + } + return false; + } } private sealed class UnpackStructureNode : StructureNode @@ -225,6 +274,35 @@ namespace FlaxEditor.Surface.Archetypes : base(id, context, nodeArch, groupArch, true) { } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + var typeName = (string)nodeArch.DefaultValues[0]; + var type = TypeUtils.GetType(typeName); + if (type) + { + if (VisjectSurface.FullCastCheck(type, outputType, hint)) + return true; + } + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + var typeName = (string)nodeArch.DefaultValues[0]; + var type = TypeUtils.GetType(typeName); + if (type) + { + var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray(); + var fieldsLength = fields.Length; + for (var i = 0; i < fieldsLength; i++) + { + if (VisjectSurface.FullCastCheck(fields[i].ValueType, inputType, hint)) + return true; + } + } + return false; + } } /// @@ -351,6 +429,8 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 26, Title = "Pack Structure", Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch), + IsInputCompatible = PackStructureNode.IsInputCompatible, + IsOutputCompatible = PackStructureNode.IsOutputCompatible, Description = "Makes the structure data to from the components.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(180, 20), @@ -461,6 +541,8 @@ namespace FlaxEditor.Surface.Archetypes TypeID = 36, Title = "Unpack Structure", Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch), + IsInputCompatible = UnpackStructureNode.IsInputCompatible, + IsOutputCompatible = UnpackStructureNode.IsOutputCompatible, Description = "Breaks the structure data to allow extracting components from it.", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI, Size = new Float2(180, 20), From 2e09c4fb6381aa91e64f16ce0558a66ab0856a7b Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 20:22:49 +0200 Subject: [PATCH 20/23] - Made visject items a tiny tiny tiny bit taller --- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 70c60f9bf..28a9392b2 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -58,7 +58,7 @@ namespace FlaxEditor.Surface.ContextMenu /// The group archetype. /// The archetype. public VisjectCMItem(VisjectCMGroup group, GroupArchetype groupArchetype, NodeArchetype archetype) - : base(0, 0, 120, 12) + : base(0, 0, 120, 14) { Group = group; _groupArchetype = groupArchetype; From aca6d7110d7d6504ef938baa195840a5eea77755 Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 20:27:51 +0200 Subject: [PATCH 21/23] - Cleanup and comments --- Source/Editor/Surface/Archetypes/Function.cs | 2 +- Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs | 4 ++-- Source/Editor/Surface/NodeArchetype.cs | 6 +++--- Source/Editor/Surface/VisjectSurface.Connecting.cs | 7 +++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index cf680c06e..d56d6d790 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -773,7 +773,7 @@ namespace FlaxEditor.Surface.Archetypes Values[4] = GetSignatureData(memberInfo, memberInfo.GetParameters()); } } - + private SignatureInfo LoadSignature() { var signature = new SignatureInfo(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index 9ab67f856..aab9f3112 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -153,10 +153,10 @@ namespace FlaxEditor.Surface.ContextMenu // Hide group if none of the items matched the filter Visible = false; } - + Profiler.EndEvent(); } - + /// /// Updates the sorting of the s of this /// Also updates the diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index 1a7f88f20..e2a9c84fa 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.Surface /// All = Scalar | Vector | Enum | Anything | Value | Array | Dictionary, } - + /// /// Surface node archetype description. /// @@ -123,7 +123,7 @@ namespace FlaxEditor.Surface /// Custom set of flags. /// public NodeFlags Flags; - + /// /// Title text. /// @@ -166,7 +166,7 @@ namespace FlaxEditor.Surface /// Connections hints. /// public ConnectionsHint ConnectionsHints; - + /// /// Array with independent boxes IDs. /// diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 9ca4f82e8..145bdfedf 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -200,6 +200,13 @@ namespace FlaxEditor.Surface return false; } + /// + /// Checks if a type is compatible with another type and can be casted by using a connection hint + /// + /// Source type + /// Target type + /// Connection hint + /// True if any method of casting or compatibility check succeeds public static bool FullCastCheck(ScriptType from, ScriptType to, ConnectionsHint hint) { // Yes, from and to are switched on purpose From 07d13f0144e115ce2eed05c1b9a7db4f7473281f Mon Sep 17 00:00:00 2001 From: Nils Hausfeld Date: Sat, 30 Sep 2023 23:06:26 +0200 Subject: [PATCH 22/23] - Implemented very basic support for method override nodes - Cleanup --- Source/Editor/Surface/Archetypes/Function.cs | 12 ++++++++++++ Source/Editor/Surface/VisualScriptSurface.cs | 1 - 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index d56d6d790..de89ab50c 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -744,6 +744,16 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } + + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) + { + return false; + } + + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) + { + return inputType.IsVoid; + } } private sealed class InvokeMethodNode : SurfaceNode @@ -2460,6 +2470,8 @@ namespace FlaxEditor.Surface.Archetypes Title = string.Empty, Description = "Overrides the base class method with custom implementation", Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI | NodeFlags.NoSpawnViaPaste, + IsInputCompatible = MethodOverrideNode.IsInputCompatible, + IsOutputCompatible = MethodOverrideNode.IsOutputCompatible, Size = new Float2(240, 60), DefaultValues = new object[] { diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index 04c4da9d2..e961f686d 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -589,7 +589,6 @@ namespace FlaxEditor.Surface node.DefaultValues[0] = name; node.DefaultValues[1] = parameters.Length; node.Title = "Override " + name; - node.Tag = member; nodes.Add(node); } } From 92f677f2380687a69738c6c0ff1c4321601168ac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Oct 2023 22:25:27 +0200 Subject: [PATCH 23/23] Codestyle formatting #1522 --- Source/Editor/Scripting/ScriptType.cs | 2 +- Source/Editor/Surface/Archetypes/Function.cs | 62 +++++++++---------- Source/Editor/Surface/Archetypes/Packing.cs | 10 +-- .../Editor/Surface/ContextMenu/VisjectCM.cs | 14 ++--- .../Surface/ContextMenu/VisjectCMGroup.cs | 4 +- .../Surface/ContextMenu/VisjectCMItem.cs | 22 +++---- Source/Editor/Surface/Elements/Box.cs | 2 +- Source/Editor/Surface/Elements/OutputBox.cs | 16 ++--- Source/Editor/Surface/NodeArchetype.cs | 6 +- Source/Editor/Surface/SurfaceUtils.cs | 4 +- .../Surface/VisjectSurface.Connecting.cs | 4 +- 11 files changed, 74 insertions(+), 72 deletions(-) diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index e3de348d2..b0c3a36dd 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -1393,7 +1393,7 @@ namespace FlaxEditor.Scripting return _custom.GetMembers(name, MemberTypes.Method, bindingAttr).FirstOrDefault(); return ScriptMemberInfo.Null; } - + /// /// Basic check to see if a type could be casted to another type /// diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index de89ab50c..e9d73ae68 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -744,12 +744,12 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { return inputType.IsVoid; @@ -1161,48 +1161,48 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; - + if (!memberInfo.IsStatic) { if (VisjectSurface.FullCastCheck(memberInfo.DeclaringType, outputType, hint)) return true; } - + var parameters = memberInfo.GetParameters(); bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid); if (outputType.IsVoid) return !isPure; - + foreach (var param in parameters) { - if(param.IsOut) + if (param.IsOut) continue; if (VisjectSurface.FullCastCheck(param.Type, outputType, hint)) return true; } return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { if (nodeArch.Tag is not ScriptMemberInfo memberInfo) return false; if (VisjectSurface.FullCastCheck(memberInfo.ValueType, inputType, hint)) return true; - + var parameters = memberInfo.GetParameters(); bool isPure = (parameters.Length == 0 && !memberInfo.ValueType.IsVoid); if (inputType.IsVoid) return !isPure; - + foreach (var param in memberInfo.GetParameters()) { - if(!param.IsOut) + if (!param.IsOut) continue; if (VisjectSurface.FullCastCheck(param.Type, inputType, hint)) return true; @@ -1835,12 +1835,12 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { return inputType.IsVoid; @@ -1981,19 +1981,19 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get " + SurfaceUtils.GetMethodDisplayName((string)Values[1]); UpdateSignature(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); if (scriptType == ScriptType.Null) return false; - + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var member in members) { if (!SurfaceUtils.IsValidVisualScriptField(member)) continue; - + if (member) { if (!member.IsStatic && VisjectSurface.FullCastCheck(scriptType, outputType, hint)) @@ -2007,22 +2007,22 @@ namespace FlaxEditor.Surface.Archetypes } break; } - + return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); if (scriptType == ScriptType.Null) return false; - + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var member in members) { if (!SurfaceUtils.IsValidVisualScriptField(member)) continue; - + if (member) { if (VisjectSurface.FullCastCheck(member.ValueType, inputType, hint)) @@ -2092,22 +2092,22 @@ namespace FlaxEditor.Surface.Archetypes Title = "Set " + SurfaceUtils.GetMethodDisplayName((string)Values[1]); UpdateSignature(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { - if(outputType.IsVoid) + if (outputType.IsVoid) return true; - + var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); if (scriptType == ScriptType.Null) return false; - + var members = scriptType.GetMembers((string)nodeArch.DefaultValues[1], MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var member in members) { if (!SurfaceUtils.IsValidVisualScriptField(member)) continue; - + if (member) { if (VisjectSurface.FullCastCheck(member.ValueType, outputType, hint)) @@ -2126,10 +2126,10 @@ namespace FlaxEditor.Surface.Archetypes } break; } - + return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { return inputType.IsVoid; @@ -2352,13 +2352,13 @@ namespace FlaxEditor.Surface.Archetypes base.OnDestroy(); } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { // Event based nodes always have a pulse input, so it's always compatible with void if (outputType.IsVoid) return true; - + var eventName = (string)nodeArch.DefaultValues[1]; var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); @@ -2372,13 +2372,13 @@ namespace FlaxEditor.Surface.Archetypes } return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { // Event based nodes always have a pulse output, so it's always compatible with void if (inputType.IsVoid) return true; - + var eventName = (string)nodeArch.DefaultValues[1]; var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index ccabaaa6d..e61b8e0df 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -207,13 +207,13 @@ namespace FlaxEditor.Surface.Archetypes AddElement(box); } } - + protected static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { // Event based nodes always have a pulse input, so it's always compatible with void if (outputType.IsVoid) return true; - + var eventName = (string)nodeArch.DefaultValues[1]; var eventType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]); var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); @@ -236,7 +236,7 @@ namespace FlaxEditor.Surface.Archetypes : base(id, context, nodeArch, groupArch, false) { } - + internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint) { var typeName = (string)nodeArch.DefaultValues[0]; @@ -253,7 +253,7 @@ namespace FlaxEditor.Surface.Archetypes } return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { var typeName = (string)nodeArch.DefaultValues[0]; @@ -286,7 +286,7 @@ namespace FlaxEditor.Surface.Archetypes } return false; } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint) { var typeName = (string)nodeArch.DefaultValues[0]; diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 36f71a1a7..342429fc8 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -164,7 +164,7 @@ namespace FlaxEditor.Surface.ContextMenu HorizontalAlignment = TextAlignment.Far, Font = titleFontReference, }; - + _contextSensitiveToggle = new CheckBox { Width = 20, @@ -174,7 +174,7 @@ namespace FlaxEditor.Surface.ContextMenu Checked = _contextSensitiveSearchEnabled, }; _contextSensitiveToggle.StateChanged += OnContextSensitiveToggleStateChanged; - + // Search box _searchBox = new SearchBox(false, 2, 22) { @@ -313,7 +313,7 @@ namespace FlaxEditor.Surface.ContextMenu OnSearchFilterChanged(); } } - else if(_contextSensitiveSearchEnabled) + else if (_contextSensitiveSearchEnabled) { group.EvaluateVisibilityWithBox(_selectedBox); } @@ -350,8 +350,8 @@ namespace FlaxEditor.Surface.ContextMenu Parent = group }; } - if(_contextSensitiveSearchEnabled) - group.EvaluateVisibilityWithBox(_selectedBox); + if (_contextSensitiveSearchEnabled) + group.EvaluateVisibilityWithBox(_selectedBox); group.SortChildren(); group.Parent = _groupsPanel; _groups.Add(group); @@ -459,7 +459,7 @@ namespace FlaxEditor.Surface.ContextMenu // Skip events during setup or init stuff if (IsLayoutLocked) return; - + Profiler.BeginEvent("VisjectCM.OnContextSensitiveToggleStateChanged"); _contextSensitiveSearchEnabled = checkBox.Checked; UpdateFilters(); @@ -551,7 +551,7 @@ namespace FlaxEditor.Surface.ContextMenu for (int i = 0; i < _groups.Count; i++) { _groups[i].ResetView(); - if(_contextSensitiveSearchEnabled) + if (_contextSensitiveSearchEnabled) _groups[i].EvaluateVisibilityWithBox(_selectedBox); } UnlockChildrenRecursive(); diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs index aab9f3112..fd825cdff 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Surface.ContextMenu } Profiler.BeginEvent("VisjectCMGroup.EvaluateVisibilityWithBox"); - + bool isAnyVisible = false; for (int i = 0; i < _children.Count; i++) { @@ -142,7 +142,7 @@ namespace FlaxEditor.Surface.ContextMenu isAnyVisible |= item.Visible; } } - + // Update itself if (isAnyVisible) { diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 28a9392b2..b512f6946 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Surface.ContextMenu { // Is compatible if box is null for reset reasons if (startBox == null) - return true; + return true; if (_archetype == null) return false; @@ -112,7 +112,7 @@ namespace FlaxEditor.Surface.ContextMenu if (startBox.IsOutput && _archetype.IsInputCompatible != null) { isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints); - } + } else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null) { isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints); @@ -122,23 +122,23 @@ namespace FlaxEditor.Surface.ContextMenu // Check compatibility based on the defined elements in the archetype. This handles all the default groups and items isCompatible = CheckElementsCompatibility(startBox); } - + return isCompatible; } - + private bool CheckElementsCompatibility(Box startBox) { bool isCompatible = false; foreach (NodeElementArchetype element in _archetype.Elements) { // Ignore all elements that aren't inputs or outputs (e.g. input fields) - if(element.Type != NodeElementType.Output && element.Type != NodeElementType.Input) + if (element.Type != NodeElementType.Output && element.Type != NodeElementType.Input) continue; - + // Ignore elements with the same direction as the box if ((startBox.IsOutput && element.Type == NodeElementType.Output) || (!startBox.IsOutput && element.Type == NodeElementType.Input)) continue; - + ScriptType fromType; ScriptType toType; ConnectionsHint hint; @@ -154,13 +154,13 @@ namespace FlaxEditor.Surface.ContextMenu toType = element.ConnectionsType; hint = startBox.ParentNode.Archetype.ConnectionsHints; } - + isCompatible |= VisjectSurface.FullCastCheck(fromType, toType, hint); } return isCompatible; } - + /// /// Updates the filter. /// @@ -176,7 +176,7 @@ namespace FlaxEditor.Surface.ContextMenu return; } } - + _isStartsWithMatch = _isFullMatch = false; if (string.IsNullOrEmpty(filterText)) { @@ -242,7 +242,7 @@ namespace FlaxEditor.Surface.ContextMenu Data = data; } - else if(!groupHeaderMatches) + else if (!groupHeaderMatches) { // Hide _highlights?.Clear(); diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 6a265a21e..a03c8f701 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -231,7 +231,7 @@ namespace FlaxEditor.Surface.Elements } // Check using connection hints - if(VisjectSurface.IsTypeCompatible(Archetype.ConnectionsType, type, ParentNode.Archetype.ConnectionsHints)) + if (VisjectSurface.IsTypeCompatible(Archetype.ConnectionsType, type, ParentNode.Archetype.ConnectionsHints)) return true; // Check independent and if there is box with bigger potential because it may block current one from changing type diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index cf78b559c..8ef64ec77 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.Surface.Elements { // Calculate control points CalculateBezierControlPoints(start, end, out var control1, out var control2); - + // Draw line Render2D.DrawBezier(start, control1, control2, end, color, thickness); @@ -54,16 +54,16 @@ namespace FlaxEditor.Surface.Elements const float maxControlLength = 150f; var dst = (end - start).Length; var yDst = Mathf.Abs(start.Y - end.Y); - + // Calculate control points var minControlDst = dst * 0.5f; var maxControlDst = Mathf.Max(Mathf.Min(maxControlLength, dst), minControlLength); var controlDst = Mathf.Lerp(minControlDst, maxControlDst, Mathf.Clamp(yDst / minControlLength, 0f, 1f)); - + control1 = new Float2(start.X + controlDst, start.Y); control2 = new Float2(end.X - controlDst, end.Y); } - + /// /// Checks if a point intersects a connection /// @@ -86,11 +86,13 @@ namespace FlaxEditor.Surface.Elements public static bool IntersectsConnection(ref Float2 start, ref Float2 end, ref Float2 point, float distance) { // Pretty much a point in rectangle check - if ((point.X - start.X) * (end.X - point.X) < 0) return false; + if ((point.X - start.X) * (end.X - point.X) < 0) + return false; float offset = Mathf.Sign(end.Y - start.Y) * distance; - if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false; - + if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) + return false; + float squaredDistance = distance; CalculateBezierControlPoints(start, end, out var control1, out var control2); diff --git a/Source/Editor/Surface/NodeArchetype.cs b/Source/Editor/Surface/NodeArchetype.cs index e2a9c84fa..fe54268e2 100644 --- a/Source/Editor/Surface/NodeArchetype.cs +++ b/Source/Editor/Surface/NodeArchetype.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Surface /// Checks if the given type is compatible with the given node archetype. Used for custom nodes /// public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint); - + /// /// Unique node type ID within a single group. /// @@ -108,12 +108,12 @@ namespace FlaxEditor.Surface /// Function for asynchronously loaded nodes to check if input ports are compatible, for filtering. /// public IsCompatible IsInputCompatible; - + /// /// Function for asynchronously loaded nodes to check if output ports are compatible, for filtering. /// public IsCompatible IsOutputCompatible; - + /// /// Default initial size of the node. /// diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index 08b9c6d63..5f4c3ef07 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -406,8 +406,8 @@ namespace FlaxEditor.Surface internal static bool IsValidVisualScriptType(ScriptType scriptType) { - if (!scriptType.IsPublic || - scriptType.HasAttribute(typeof(HideInEditorAttribute), true) || + if (!scriptType.IsPublic || + scriptType.HasAttribute(typeof(HideInEditorAttribute), true) || scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)) return false; if (scriptType.IsGenericType) diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 145bdfedf..dd34f9c7c 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -212,12 +212,12 @@ namespace FlaxEditor.Surface // Yes, from and to are switched on purpose if (CanUseDirectCastStatic(to, from, false)) return true; - if(IsTypeCompatible(from, to, hint)) + if (IsTypeCompatible(from, to, hint)) return true; // Same here return to.CanCastTo(from); } - + /// /// Checks if can use direct conversion from one type to another. ///