您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
245 行
6.2 KiB
245 行
6.2 KiB
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor.Experimental.UIElements.GraphView;
|
|
using UnityEngine.Experimental.UIElements;
|
|
using UnityEditor;
|
|
using System.Reflection;
|
|
using System;
|
|
using System.Linq;
|
|
using UnityEditorInternal;
|
|
|
|
using StatusFlags = UnityEngine.Experimental.UIElements.DropdownMenu.MenuAction.StatusFlags;
|
|
using NodeView = UnityEditor.Experimental.UIElements.GraphView.Node;
|
|
|
|
namespace GraphProcessor
|
|
{
|
|
[NodeCustomEditor(typeof(BaseNode))]
|
|
public class BaseNodeView : NodeView
|
|
{
|
|
public BaseNode nodeTarget;
|
|
|
|
public List< Port > inputPorts = new List< Port >();
|
|
public List< Port > outputPorts = new List< Port >();
|
|
|
|
public BaseGraphView owner { private set; get; }
|
|
|
|
protected Dictionary< string, Port > portsPerFieldName = new Dictionary< string, Port >();
|
|
|
|
protected VisualElement controlsContainer;
|
|
protected VisualElement debugContainer;
|
|
|
|
Label computeOrderLabel = new Label();
|
|
|
|
#region Initialization
|
|
|
|
public void Initialize(BaseGraphView owner, BaseNode node)
|
|
{
|
|
nodeTarget = node;
|
|
this.owner = owner;
|
|
|
|
owner.computeOrderUpdated += ComputeOrderUpdatedCallback;
|
|
|
|
AddStyleSheetPath("GraphProcessorStyles/BaseNodeView");
|
|
|
|
InitializePorts();
|
|
InitializeView();
|
|
InitializeDebug();
|
|
|
|
Enable();
|
|
|
|
this.RefreshPorts();
|
|
}
|
|
|
|
void InitializePorts()
|
|
{
|
|
foreach (var fieldInfo in nodeTarget.nodeFields)
|
|
{
|
|
// This will automatically create our visual ports
|
|
PortBehaviorFactory.CreatePortBehavior(
|
|
this,
|
|
fieldInfo.Value.info,
|
|
fieldInfo.Value.input ? Direction.Input : Direction.Output,
|
|
owner.connectorListener,
|
|
fieldInfo.Value.isMultiple,
|
|
fieldInfo.Value.name
|
|
);
|
|
}
|
|
}
|
|
|
|
void InitializeView()
|
|
{
|
|
controlsContainer = new VisualElement{ name = "controls" };
|
|
mainContainer.Add(controlsContainer);
|
|
|
|
debugContainer = new VisualElement{ name = "debug" };
|
|
mainContainer.Add(debugContainer);
|
|
|
|
title = (string.IsNullOrEmpty(nodeTarget.name)) ? nodeTarget.GetType().Name : nodeTarget.name;
|
|
|
|
SetPosition(nodeTarget.position);
|
|
}
|
|
|
|
void InitializeDebug()
|
|
{
|
|
ComputeOrderUpdatedCallback();
|
|
debugContainer.Add(computeOrderLabel);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region API
|
|
|
|
public Port GetPortFromFieldName(string fieldName)
|
|
{
|
|
Port ret;
|
|
|
|
portsPerFieldName.TryGetValue(fieldName, out ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
public PortView AddPort(FieldInfo fieldInfo, Direction direction, EdgeConnectorListener listener, bool isMultiple = false, string name = null)
|
|
{
|
|
// TODO: hardcoded value
|
|
PortView p = new PortView(Orientation.Horizontal, direction, fieldInfo, listener);
|
|
|
|
if (p.direction == Direction.Input)
|
|
{
|
|
inputPorts.Add(p);
|
|
inputContainer.Add(p);
|
|
}
|
|
else
|
|
{
|
|
outputPorts.Add(p);
|
|
outputContainer.Add(p);
|
|
}
|
|
|
|
p.Initialize(this, isMultiple, name);
|
|
|
|
portsPerFieldName[p.fieldName] = p;
|
|
|
|
return p;
|
|
}
|
|
|
|
public void RemovePort(PortView p)
|
|
{
|
|
if (p.direction == Direction.Input)
|
|
{
|
|
inputPorts.Remove(p);
|
|
inputContainer.Remove(p);
|
|
|
|
}
|
|
else
|
|
{
|
|
outputPorts.Remove(p);
|
|
outputContainer.Remove(p);
|
|
}
|
|
|
|
portsPerFieldName.Remove(p.fieldName);
|
|
}
|
|
|
|
public void OpenNodeViewScript()
|
|
{
|
|
var scriptPath = NodeProvider.GetNodeViewScript(GetType());
|
|
|
|
if (scriptPath != null)
|
|
InternalEditorUtility.OpenFileAtLineExternal(scriptPath, 0);
|
|
}
|
|
|
|
public void OpenNodeScript()
|
|
{
|
|
var scriptPath = NodeProvider.GetNodeScript(nodeTarget.GetType());
|
|
|
|
if (scriptPath != null)
|
|
InternalEditorUtility.OpenFileAtLineExternal(scriptPath, 0);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Callbacks & Overrides
|
|
|
|
void ComputeOrderUpdatedCallback()
|
|
{
|
|
//Update debug compute order
|
|
computeOrderLabel.text = "Compute order: " + nodeTarget.computeOrder;
|
|
}
|
|
|
|
public virtual void Enable()
|
|
{
|
|
DrawDefaultInspector();
|
|
}
|
|
|
|
public virtual void DrawDefaultInspector()
|
|
{
|
|
var fields = nodeTarget.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
|
|
foreach (var field in fields)
|
|
{
|
|
//skip if the field is not serializable
|
|
if (!field.IsPublic && field.GetCustomAttribute(typeof(SerializeField)) == null)
|
|
continue ;
|
|
|
|
//skip if the field is an input/output and not marked as SerializedField
|
|
if (field.GetCustomAttribute(typeof(SerializeField)) == null && (field.GetCustomAttribute(typeof(InputAttribute)) != null || field.GetCustomAttribute(typeof(OutputAttribute)) != null))
|
|
continue ;
|
|
|
|
//skip if marked with NonSerialized or HideInInspector
|
|
if (field.GetCustomAttribute(typeof(System.NonSerializedAttribute)) != null || field.GetCustomAttribute(typeof(HideInInspector)) != null)
|
|
continue ;
|
|
|
|
var controlLabel = new Label(field.Name);
|
|
controlsContainer.Add(controlLabel);
|
|
|
|
var element = FieldFactory.CreateField(field, field.GetValue(nodeTarget), (newValue) => {
|
|
field.SetValue(nodeTarget, newValue);
|
|
owner.RegisterCompleteObjectUndo("Updated " + newValue);
|
|
});
|
|
|
|
if (element != null)
|
|
controlsContainer.Add(element);
|
|
}
|
|
}
|
|
|
|
public virtual void OnPortConnected(PortView port) {}
|
|
public virtual void OnPortDisconnected(PortView port) {}
|
|
|
|
public override void SetPosition(Rect newPos)
|
|
{
|
|
base.SetPosition(newPos);
|
|
|
|
nodeTarget.position = newPos;
|
|
}
|
|
|
|
public override bool expanded
|
|
{
|
|
get { return base.expanded; }
|
|
set
|
|
{
|
|
base.expanded = value;
|
|
nodeTarget.expanded = value;
|
|
}
|
|
}
|
|
|
|
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
|
{
|
|
evt.menu.AppendAction("Open Node Script", (e) => OpenNodeScript(), OpenNodeScriptStatus);
|
|
evt.menu.AppendAction("Open Node View Script", (e) => OpenNodeViewScript(), OpenNodeViewScriptStatus);
|
|
}
|
|
|
|
StatusFlags OpenNodeScriptStatus(DropdownMenu.MenuAction action)
|
|
{
|
|
if (NodeProvider.GetNodeScript(nodeTarget.GetType()) != null)
|
|
return StatusFlags.Normal;
|
|
return StatusFlags.Disabled;
|
|
}
|
|
|
|
StatusFlags OpenNodeViewScriptStatus(DropdownMenu.MenuAction action)
|
|
{
|
|
if (NodeProvider.GetNodeViewScript(GetType()) != null)
|
|
return StatusFlags.Normal;
|
|
return StatusFlags.Disabled;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|