Contextual Node Creation

This commit is contained in:
Chromium 2025-04-29 02:05:16 +01:00
parent 370504dcfc
commit 4cd9878a97
3 changed files with 194 additions and 51 deletions

View File

@ -26,25 +26,24 @@ 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<GraphConnection> connections = new List<GraphConnection>();
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);
}

View File

@ -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,10 +17,18 @@ 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<int, Type> ImportPortTypes { get; private set; }
public Dictionary<int, Type> ExportPortTypes { get; private set; }
public int portID;
public SearchContextElement(object target, string title, Dictionary<int, Type> importPortTypes,
Dictionary<int, Type> exportPortTypes, int portID)
{
this.target = target;
this.title = title;
this.ImportPortTypes = importPortTypes;
this.ExportPortTypes = exportPortTypes;
this.portID = portID;
}
}
@ -30,6 +39,7 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
public static List<SearchContextElement> elements;
private Assembly[] assemblies;
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
List<SearchTreeEntry> tree = new List<SearchTreeEntry>();
@ -37,37 +47,26 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
elements = new List<SearchContextElement>();
/*
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));
}
}
}
}*/
foreach (var type in TypeCache.GetTypesWithAttribute<NodeInfoAttribute>())
{
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
NodeInfoAttribute info = attr as NodeInfoAttribute;
var node = Activator.CreateInstance(type);
if(string.IsNullOrEmpty(info.MenuItem)) continue;
if (string.IsNullOrEmpty(info.MenuItem)) continue;
elements.Add(new SearchContextElement(node, info.MenuItem));
var Fields = type.GetFields();
Dictionary<int, Type> ImportPortTypes = new Dictionary<int, Type>();
Dictionary<int, Type> OutputPortTypes = new Dictionary<int, Type>();
foreach (var field in Fields)
{
if (field.GetCustomAttribute<Input>() != null)
ImportPortTypes.Add(ImportPortTypes.Count, field.FieldType);
if (field.GetCustomAttribute<Output>() != null)
OutputPortTypes.Add(OutputPortTypes.Count, field.FieldType);
}
elements.Add(new SearchContextElement(node, info.MenuItem, ImportPortTypes, OutputPortTypes, 0));
}
elements.Sort((entry1, entry2) =>
@ -82,8 +81,8 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
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;
}
@ -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,7 +122,8 @@ 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;
@ -134,5 +135,148 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
return true;
}
public List<SearchTreeEntry> CreateSearchTreeForEdge(SearchWindowContext context, Edge edge)
{
var tree = new List<SearchTreeEntry>();
tree.Add(new SearchTreeGroupEntry(new GUIContent("Compatible Nodes"), 0));
elements = new List<SearchContextElement>();
var groups = new List<string>();
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<NodeInfoAttribute>())
{
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
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<Input>() != null).ToList();
var outputPorts = fields.Where(f => f.GetCustomAttribute<Output>() != 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<Input>() != 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<SearchTreeEntry> 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;
}
}
}
}

View File

@ -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;