From 4cd9878a976decadb3a4712b100ec7da0379db9e Mon Sep 17 00:00:00 2001 From: Chromium <62724067+Chromum@users.noreply.github.com> Date: Tue, 29 Apr 2025 02:05:16 +0100 Subject: [PATCH] Contextual Node Creation --- ...ageProcessingGraphEdgeConnectorListener.cs | 19 +- .../ImageProcessingGraphSearchProvider.cs | 224 ++++++++++++++---- .../Windows/ImageProcessingGraphViewWindow.cs | 2 +- 3 files changed, 194 insertions(+), 51 deletions(-) diff --git a/Editor/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs b/Editor/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs index ee5e037..7c1b330 100644 --- a/Editor/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs +++ b/Editor/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs @@ -26,28 +26,27 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind public void OnDropOutsidePort(Edge edge, Vector2 position) { window.searchProvider.target = (VisualElement)window.focusController.focusedElement; - SearchWindow.Open(new SearchWindowContext(position), window.searchProvider); - + + // ⚡ Override the search window open with a customized search tree + SearchWindow.Open(new SearchWindowContext(position), new ImageProcessingGraphSearchProvider.CustomSearchProviderForEdge(window.searchProvider, edge, (IPTPort)edge.input, (IPTPort)edge.output)); + + // Remove connections as you did List connections = new List(); - foreach (var conn in window.asset.Connections) { if (conn.internalEdge == edge) - { connections.Add(conn); - } } - foreach (var VARIABLE in connections) { window.asset.Connections.Remove(VARIABLE); } - - if(edge.input != null) - if(edge.input.node != null) + + if (edge.input != null) + if (edge.input.node != null) ((ImageProcessingGraphNodeVisual)edge.input.node).ToggleExposedVariable((IPTPort)edge.input, true); } - + public void OnDrop(UnityEditor.Experimental.GraphView.GraphView graphView, Edge edge) diff --git a/Editor/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs b/Editor/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs index 4b7e2d6..605ea85 100644 --- a/Editor/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs +++ b/Editor/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs @@ -8,6 +8,7 @@ using UnityEditor; using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEngine.UIElements; +using Input = ImageProcessingGraph.Editor.Nodes.NodeAttributes.Input; namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows { @@ -16,13 +17,21 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind public object target { get; private set; } public string title { get; private set; } - public SearchContextElement(object target, string title) + public Dictionary ImportPortTypes { get; private set; } + public Dictionary ExportPortTypes { get; private set; } + public int portID; + + public SearchContextElement(object target, string title, Dictionary importPortTypes, + Dictionary exportPortTypes, int portID) { this.target = target; this.title = title; + this.ImportPortTypes = importPortTypes; + this.ExportPortTypes = exportPortTypes; + this.portID = portID; } } - + public class ImageProcessingGraphSearchProvider : ScriptableObject, ISearchWindowProvider { public ImageProcessingGraphViewWindow graph; @@ -30,44 +39,34 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind public static List elements; private Assembly[] assemblies; + public List CreateSearchTree(SearchWindowContext context) { List tree = new List(); tree.Add(new SearchTreeGroupEntry(new GUIContent("Nodes"), 0)); - - elements = new List(); - - /* - assemblies = AppDomain.CurrentDomain.GetAssemblies(); - */ - /*foreach (var assembly in assemblies) - { - foreach (Type type in assembly.GetTypes()) - { - if (type.CustomAttributes.ToList() != null) - { - var attr = type.GetCustomAttribute(typeof(NodeInfoAttribute)); - if (attr != null) - { - NodeInfoAttribute info = attr as NodeInfoAttribute; - var node = Activator.CreateInstance(type); - if(string.IsNullOrEmpty(info.MenuItem)) continue; - - elements.Add(new SearchContextElement(node, info.MenuItem)); - } - } - } - }*/ - + elements = new List(); + foreach (var type in TypeCache.GetTypesWithAttribute()) { var attr = type.GetCustomAttribute(); NodeInfoAttribute info = attr as NodeInfoAttribute; var node = Activator.CreateInstance(type); - if(string.IsNullOrEmpty(info.MenuItem)) continue; - - elements.Add(new SearchContextElement(node, info.MenuItem)); + if (string.IsNullOrEmpty(info.MenuItem)) continue; + + var Fields = type.GetFields(); + Dictionary ImportPortTypes = new Dictionary(); + Dictionary OutputPortTypes = new Dictionary(); + + foreach (var field in Fields) + { + if (field.GetCustomAttribute() != null) + ImportPortTypes.Add(ImportPortTypes.Count, field.FieldType); + if (field.GetCustomAttribute() != null) + OutputPortTypes.Add(OutputPortTypes.Count, field.FieldType); + } + + elements.Add(new SearchContextElement(node, info.MenuItem, ImportPortTypes, OutputPortTypes, 0)); } elements.Sort((entry1, entry2) => @@ -78,20 +77,20 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind for (int i = 0; i < splits1.Length; i++) { if (i >= splits2.Length) return 1; - + int value = splits1[i].CompareTo(splits2[i]); if (value != 0) { - if(splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1)) - return splits1.Length < splits2.Length ? 1 : -1; - + if (splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1)) + return splits1.Length < splits2.Length ? 1 : -1; + return value; } } return 0; }); - + List groups = new List(); foreach (var element in elements) @@ -108,12 +107,13 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind tree.Add(new SearchTreeGroupEntry(new GUIContent(groupName), i + 1)); groups.Add(groupName); } + groupName += '/'; } - + SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(entryTitle.Last())); entry.level = entryTitle.Length; - entry.userData = new SearchContextElement(element.target, element.title); + entry.userData = element; tree.Add(entry); } @@ -122,11 +122,12 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context) { - var mousePos = graph.ChangeCoordinatesTo(graph, context.screenMousePosition - graph.Window.position.position); + var mousePos = + graph.ChangeCoordinatesTo(graph, context.screenMousePosition - graph.Window.position.position); var graphMousePosition = graph.contentViewContainer.WorldToLocal(mousePos); - + SearchContextElement element = (SearchContextElement)SearchTreeEntry.userData; - + BaseImageNode node = (BaseImageNode)element.target; node.SetPosition(new Rect(graphMousePosition, new Vector2())); graph.Add(node); @@ -134,5 +135,148 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind return true; } + + public List CreateSearchTreeForEdge(SearchWindowContext context, Edge edge) + { + var tree = new List(); + tree.Add(new SearchTreeGroupEntry(new GUIContent("Compatible Nodes"), 0)); + + elements = new List(); + var groups = new List(); + + var sourcePort = edge.output ?? edge.input; + bool isSourceOutput = edge.output != null; + Type targetType = (sourcePort as IPTPort)?.portType; + + if (targetType == null) + { + Debug.LogWarning("Could not determine port type from edge!"); + return tree; + } + + foreach (var type in TypeCache.GetTypesWithAttribute()) + { + var attr = type.GetCustomAttribute(); + if (string.IsNullOrEmpty(attr.MenuItem)) continue; + + var node = Activator.CreateInstance(type); + var fields = type.GetFields(); + + // Get all ports + var inputPorts = fields.Where(f => f.GetCustomAttribute() != null).ToList(); + var outputPorts = fields.Where(f => f.GetCustomAttribute() != null).ToList(); + + // REVERSED LOGIC: + // If dragging from output, we want compatible INPUT ports + // If dragging from input, we want compatible OUTPUT ports + var compatiblePorts = isSourceOutput + ? inputPorts.Where(f => f.FieldType.IsAssignableFrom(targetType)).ToList() + : outputPorts.Where(f => targetType.IsAssignableFrom(f.FieldType)).ToList(); + + if (compatiblePorts.Count == 0) continue; + + // Build group hierarchy + var menuPath = attr.MenuItem.Split('/'); + var currentGroupPath = ""; + + for (int i = 0; i < menuPath.Length - 1; i++) + { + currentGroupPath += menuPath[i]; + if (!groups.Contains(currentGroupPath)) + { + tree.Add(new SearchTreeGroupEntry(new GUIContent(menuPath[i]), i + 1)); + groups.Add(currentGroupPath); + } + + currentGroupPath += "/"; + } + + // // Add node entry + // var nodeTitle = $"{type.Name}: {menuPath.Last()}"; + // tree.Add(new SearchTreeEntry(new GUIContent(nodeTitle)) + // { + // level = menuPath.Length, + // userData = new SearchContextElement( + // node, + // nodeTitle, + // inputPorts.ToDictionary(f => inputPorts.IndexOf(f), f => f.FieldType), + // outputPorts.ToDictionary(f => outputPorts.IndexOf(f), f => f.FieldType)) + // }); + + // Add compatible port entries + foreach (var portField in compatiblePorts) + { + var portList = portField.GetCustomAttribute() != null ? inputPorts : outputPorts; + int portIndex = portList.IndexOf(portField); + + var portTitle = $"{attr.Title}: {portField.Name}"; + tree.Add(new SearchTreeEntry(new GUIContent(portTitle)) + { + level = menuPath.Length, + userData = new SearchContextElement( + node, + portTitle, + inputPorts.ToDictionary(f => inputPorts.IndexOf(f), f => f.FieldType), + outputPorts.ToDictionary(f => outputPorts.IndexOf(f), f => f.FieldType), + portIndex) + }); + } + } + + return tree; + } + + public class CustomSearchProviderForEdge : ScriptableObject, ISearchWindowProvider + { + private readonly ImageProcessingGraphSearchProvider original; + private readonly Edge edge; + private readonly IPTPort inputPort; + private readonly IPTPort outputPort; + + public CustomSearchProviderForEdge(ImageProcessingGraphSearchProvider original, Edge edge, IPTPort inputPort, IPTPort outputPort) + { + this.original = original; + this.edge = edge; + this.inputPort = inputPort; + this.outputPort = outputPort; + } + + public List CreateSearchTree(SearchWindowContext context) + { + return original.CreateSearchTreeForEdge(context, edge); + } + + public bool OnSelectEntry(SearchTreeEntry selectedEntry, SearchWindowContext context) + { + var mousePos = + original.graph.ChangeCoordinatesTo(original.graph, context.screenMousePosition - original.graph.Window.position.position); + var graphMousePosition = original.graph.contentViewContainer.WorldToLocal(mousePos); + + SearchContextElement element = (SearchContextElement)selectedEntry.userData; + + BaseImageNode node = (BaseImageNode)element.target; + node.SetPosition(new Rect(graphMousePosition, new Vector2())); + original.graph.Add(node); + node.asset = original.graph.asset; + + + Edge edge = new Edge(); + + if (inputPort != null) + { + edge = inputPort.ConnectTo(original.graph.nodeDictionary[node.ID].OutputPorts[element.portID]); + } + else if (outputPort != null) + { + edge = outputPort.ConnectTo(original.graph.nodeDictionary[node.ID].InputPorts[element.portID]); + } + + original.graph.CreateEdge(edge); + original.graph.AddElement((GraphElement)edge); + + return true; + } + } + } } \ No newline at end of file diff --git a/Editor/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs b/Editor/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs index ba90db5..5229327 100644 --- a/Editor/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs +++ b/Editor/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs @@ -299,7 +299,7 @@ namespace ImageProcessingGraph.Editor #region Edges - void CreateEdge(Edge edge) + public void CreateEdge(Edge edge) { ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node; ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node;