using System; using System.Linq; using System.Reflection; using TNode.Editor.Inspector; using TNodeCore; using TNodeCore.Attribute.Ports; using TNodeCore.Editor.NodeGraphView; using TNodeCore.Editor.Serialization; using TNodeCore.Models; using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEngine.UIElements; namespace TNodeGraphViewImpl.Editor.NodeViews{ public abstract class BaseNodeView : Node,INodeView where T:NodeData,new(){ protected T _data; private readonly NodeInspectorInNode _nodeInspectorInNode; public IBaseDataGraphView BaseDataGraphView{ get{ var visualElement = this.GetFirstAncestorOfType() as IBaseDataGraphView; return visualElement; } } public T Data{ get => _data; set{ if(_data!=null) ((NodeDataWrapper)_data).OnValueChanged -= OnDataValueChanged; _data = value; OnDataChanged?.Invoke(value); if(_data!=null) ((NodeDataWrapper)_data).OnValueChanged += OnDataValueChanged; } } private void OnDataValueChanged(DataWrapper obj){ Refresh(); } public sealed override string title{ get => base.title; set => base.title = value; } public event System.Action OnDataChanged; protected BaseNodeView(){ OnDataChanged+=OnDataChangedHandler; _nodeInspectorInNode = new NodeInspectorInNode(){ name = "nodeInspectorInNode" }; this.extensionContainer.Add(_nodeInspectorInNode); BuildDoubleClickRename(); } private void OnDataChangedHandler(T obj){ this.title = _data.nodeName; if (_nodeInspectorInNode != null){ _nodeInspectorInNode.Data = obj; } BuildInputAndOutputPort(); this.expanded = true; this.RefreshExpandedState(); } protected virtual string BuildPortName(PortAttribute portAttribute,PropertyInfo propertyInfo,params object[] args){ switch (portAttribute.NameHandling){ case PortNameHandling.Auto: return portAttribute.Name.Trim(' ').Length>0?portAttribute.Name:propertyInfo.Name; case PortNameHandling.Manual: return portAttribute.Name; case PortNameHandling.MemberName: return propertyInfo.Name; case PortNameHandling.Format: return String.Format(propertyInfo.Name, args); case PortNameHandling.MemberType: return propertyInfo.PropertyType.Name; default: throw new ArgumentOutOfRangeException(); } } protected virtual Type BuildPortType(PortAttribute portAttribute,PropertyInfo propertyInfo){ switch (portAttribute.TypeHandling){ case TypeHandling.Declared : return propertyInfo.PropertyType; case TypeHandling.Implemented: return propertyInfo.GetValue(_data)?.GetType(); case TypeHandling.Specified: return portAttribute.HandledType??typeof(object); default: throw new ArgumentOutOfRangeException(); } } /// /// of course you can override this method to build your own port builder /// protected virtual void BuildInputAndOutputPort(){ var propertyInfos = _data.GetType().GetProperties(); foreach (var propertyInfo in propertyInfos){ if (propertyInfo.GetCustomAttributes(typeof(OutputAttribute),true).FirstOrDefault() is OutputAttribute attribute){ Port port = InstantiatePort(Orientation.Horizontal, Direction.Output,Port.Capacity.Multi,BuildPortType(attribute,propertyInfo)); this.outputContainer.Add(port); var portName = BuildPortName(attribute,propertyInfo); port.portName = portName; port.name = portName; } } foreach (var propertyInfo in propertyInfos){ if(propertyInfo.GetCustomAttributes(typeof(InputAttribute),true).FirstOrDefault() is InputAttribute attribute){ Port port = InstantiatePort(Orientation.Horizontal, Direction.Input,Port.Capacity.Single,BuildPortType(attribute,propertyInfo)); this.inputContainer.Add(port); var portName = BuildPortName(attribute,propertyInfo); port.portName = portName; port.name = portName; } } } public void StartARenameTitleTextField(){ var textField = new TextField{ value = title, style ={ //Make the text filed overlap the title container position = Position.Absolute, left = 0, top = 0, width = titleContainer.layout.width, height = titleContainer.layout.height } }; textField.StretchToParentSize(); textField.RegisterValueChangedCallback(evt2 => { title = evt2.newValue; }); textField.RegisterCallback(evt2 => { title = textField.text; ((NodeDataWrapper)_data).SetValue("nodeName",textField.text); textField.RemoveFromHierarchy(); }); //if enter is pressed ,set the title and remove the text field textField.RegisterCallback(evt2 => { if (evt2.keyCode == KeyCode.Return){ title = textField.text; ((NodeDataWrapper)_data).SetValue("nodeName",textField.text); textField.RemoveFromHierarchy(); } }); titleContainer.Add(textField); textField.Focus(); } private void BuildDoubleClickRename(){ //when double click titleContainer ,create a textfield to rename the node titleContainer.RegisterCallback(evt => { if (evt.clickCount == 2){ StartARenameTitleTextField(); } }); } public void SetNodeData(NodeData nodeData){ Data = (T)nodeData; } public NodeData GetNodeData(){ return _data; } public void OnDataModified(){ Refresh(); } public void Refresh(){ title = _data.nodeName; } } public interface IBaseNodeView{ public void SetNodeData(NodeData nodeData); public NodeData GetNodeData(); public void OnDataModified(); IBaseDataGraphView BaseDataGraphView{ get; } } public interface INodeView:IBaseNodeView where T:NodeData,new(){ public T Data{ get; set; } } }