diff --git a/Sample/Editor/HelloEditor.cs b/Sample/Editor/HelloEditor.cs index 84da187..d342d59 100644 --- a/Sample/Editor/HelloEditor.cs +++ b/Sample/Editor/HelloEditor.cs @@ -18,5 +18,9 @@ namespace Sample.Editor{ } return false; } + + public HelloEditor(){ + + } } } \ No newline at end of file diff --git a/Sample/Editor/HelloGraphView.cs b/Sample/Editor/HelloGraphView.cs index 7bb5ef4..4185407 100644 --- a/Sample/Editor/HelloGraphView.cs +++ b/Sample/Editor/HelloGraphView.cs @@ -9,5 +9,7 @@ namespace Sample.Editor{ public override void OnGraphViewCreate(){ CreateInspector(); } + + } } \ No newline at end of file diff --git a/TNode/Attribute/ShowInNodeViewAttribute.cs b/TNode/Attribute/ShowInNodeViewAttribute.cs new file mode 100644 index 0000000..f2ffde9 --- /dev/null +++ b/TNode/Attribute/ShowInNodeViewAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace TNode.Attribute{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + public class ShowInNodeViewAttribute:System.Attribute{ + + } +} \ No newline at end of file diff --git a/TNode/Attribute/ShowInNodeViewAttribute.cs.meta b/TNode/Attribute/ShowInNodeViewAttribute.cs.meta new file mode 100644 index 0000000..810a289 --- /dev/null +++ b/TNode/Attribute/ShowInNodeViewAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3486b937bc1e4a95aa03e842a23fdf72 +timeCreated: 1656584304 \ No newline at end of file diff --git a/TNode/Editor/BaseViews/DataGraphView.cs b/TNode/Editor/BaseViews/DataGraphView.cs index c760bcc..e82d897 100644 --- a/TNode/Editor/BaseViews/DataGraphView.cs +++ b/TNode/Editor/BaseViews/DataGraphView.cs @@ -178,12 +178,24 @@ namespace TNode.Editor.BaseViews{ public virtual void CreateInspector(){ NodeInspector nodeInspector = new NodeInspector(); - nodeInspector.SetPosition(new Rect(200,200,200,600)); this.Add(nodeInspector); _nodeInspector = nodeInspector; _isInspectorOn = true; } + public virtual void DestroyInspector(){ + if(_nodeInspector!=null){ + this.Remove(_nodeInspector); + _nodeInspector = null; + } + _isInspectorOn = false; + } + public virtual void SetInspector(NodeInspector nodeInspector){ + _nodeInspector = nodeInspector; + if (!_isInspectorOn){ + _isInspectorOn = true; + } + } public virtual void OnGraphViewCreate(){ diff --git a/TNode/Editor/BaseViews/NodeView.cs b/TNode/Editor/BaseViews/NodeView.cs index ae54853..3abe947 100644 --- a/TNode/Editor/BaseViews/NodeView.cs +++ b/TNode/Editor/BaseViews/NodeView.cs @@ -1,7 +1,9 @@ -using TNode.Models; +using TNode.Editor.Inspector; +using TNode.Models; using UnityEditor; using UnityEditor.Experimental.GraphView; using UnityEditor.UIElements; +using UnityEngine; namespace TNode.Editor.BaseViews{ @@ -9,11 +11,14 @@ namespace TNode.Editor.BaseViews{ public abstract class NodeView : Node,INodeView where T:NodeData,new(){ protected T _data; + private readonly NodeInspectorInNode _nodeInspectorInNode; + public T Data{ get => _data; set{ _data = value; OnDataChanged?.Invoke(value); + } } public sealed override string title{ @@ -24,14 +29,25 @@ namespace TNode.Editor.BaseViews{ protected NodeView(){ OnDataChanged+=OnDataChangedHandler; + + _nodeInspectorInNode = new NodeInspectorInNode(){ + name = "nodeInspectorInNode" + }; + this.extensionContainer.Add(_nodeInspectorInNode); } private void OnDataChangedHandler(T obj){ this.title = _data.nodeName; + if (_nodeInspectorInNode != null){ + _nodeInspectorInNode.Data = obj; + this.RefreshExpandedState(); + this.expanded = true; + } } public void SetNodeData(NodeData nodeData){ Data = (T)nodeData; + } public NodeData GetNodeData(){ diff --git a/TNode/Editor/Cache/NodeEditorExtensions.cs b/TNode/Editor/Cache/NodeEditorExtensions.cs index 50c18de..def3bc3 100644 --- a/TNode/Editor/Cache/NodeEditorExtensions.cs +++ b/TNode/Editor/Cache/NodeEditorExtensions.cs @@ -5,6 +5,7 @@ using TNode.Attribute; using TNode.BaseViews; using TNode.Editor; using TNode.Editor.BaseViews; +using TNode.Editor.Inspector; using TNode.Models; using UnityEditor.Experimental.GraphView; using UnityEngine; @@ -57,14 +58,13 @@ namespace TNode.Cache{ } } } - + private readonly Type[] _acceptedTypesForGenericToSpecific = new Type[]{typeof(NodeView<>),typeof(DataGraphView<>),typeof(InspectorItem<>)}; private void SetNodeComponentAttribute(Type type){ foreach (var attribute in type.GetCustomAttributes(typeof(NodeComponentAttribute), false)){ //fetch this type 's parent class var parent = type.BaseType; //Check if this type is a generic type and is a generic type of NodeView or DataGraphView - if (parent is{IsGenericType: true} && (parent.GetGenericTypeDefinition() == typeof(NodeView<>) || - parent.GetGenericTypeDefinition() == typeof(DataGraphView<>))){ + if (parent is{IsGenericType: true} && _acceptedTypesForGenericToSpecific.Contains(parent.GetGenericTypeDefinition())){ //Get the generic type of this type //Add this type to the dictionary FromGenericToSpecific.Add(parent, type); diff --git a/TNode/Editor/GraphEditor.cs b/TNode/Editor/GraphEditor.cs index f80078d..4e7edae 100644 --- a/TNode/Editor/GraphEditor.cs +++ b/TNode/Editor/GraphEditor.cs @@ -2,6 +2,7 @@ using Codice.CM.Common; using TNode.BaseViews; using TNode.Cache; using TNode.Editor.BaseViews; +using TNode.Editor.Inspector; using TNode.Editor.Model; using TNode.Models; using UnityEditor; @@ -35,20 +36,16 @@ namespace TNode.Editor{ DefineGraphEditorActions(); OnCreate(); } + private void BuildGraphView(){ _graphView = NodeEditorExtensions.CreateInstance>(); rootVisualElement.Add(_graphView); _graphView.StretchToParentSize(); - - _graphView.ConstructViewContextualMenu(evt => { - //Current issue is that the search window don't show up at the exact position of the mouse click by dma.eventInfo.mousePosition //So I have to manually set the position of the search window to fit the mouse click position by add an offset driven by Editor's position //Maybe a better way exists to fix this issue Vector2 editorPosition = this.position.position; - - evt.menu.AppendAction("Create Node", dma => { var dmaPos = dma.eventInfo.mousePosition+editorPosition; SearchWindowContext searchWindowContext = new SearchWindowContext(dmaPos,200,200); diff --git a/TNode/Editor/Inspector/DefaultInspectorItemFactory.cs b/TNode/Editor/Inspector/DefaultInspectorItemFactory.cs deleted file mode 100644 index 2d2d7ef..0000000 --- a/TNode/Editor/Inspector/DefaultInspectorItemFactory.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using TNode.Cache; -using TNode.Editor.Inspector.InspectorImplementation; -using Unity.VisualScripting; -using UnityEditor; -using UnityEngine; -using UnityEngine.UIElements; - -namespace TNode.Editor.Inspector{ - public class DefaultInspectorItemFactory{ - - public InspectorItem Create(){ - //Check type of GraphDataType - var hasSpecificType = NodeEditorExtensions.HasSpecificType>(); - if (hasSpecificType){ - return NodeEditorExtensions.CreateInstance>(); - } - else{ - return DefaultInspectorItem(); - } - } - - public static InspectorItem DefaultInspectorItem(){ - DefaultInspectorItem item = new DefaultInspectorItem(); - if (typeof(string) == typeof(T)){ - var textField = new TextField(){ - name = "StringTextField" - }; - item.foldOut.Add(textField); - textField.RegisterCallback>(e => { - Debug.Log(item.BindingNodeData); - Debug.Log(item.BindingPath); - item.BindingNodeData.GetType().GetField(item.BindingPath).SetValue(item.BindingNodeData, e.newValue); - if (item.parent.parent is NodeInspector nodeInspector){ - Debug.Log("item 's parent 's parent is exactly a node inspector"); - nodeInspector.NodeView.OnDataModified(); - } - - }); - } - return item; - - } - } -} - \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/DefaultInspectorItem.cs b/TNode/Editor/Inspector/InspectorImplementation/DefaultInspectorItem.cs deleted file mode 100644 index bf99501..0000000 --- a/TNode/Editor/Inspector/InspectorImplementation/DefaultInspectorItem.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UnityEngine.UIElements; - -namespace TNode.Editor.Inspector.InspectorImplementation{ - public class DefaultInspectorItem:InspectorItem{ - public readonly Foldout foldOut; - public DefaultInspectorItem():base(){ - foldOut = new Foldout{ - text = "" - }; - this.Add(foldOut); - OnValueChanged += () => { - foldOut.text = this.BindingPath; - var textField = this.Q(); - if(textField != null){ - textField.value = this.Value.ToString(); - } - }; - } - } -} \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs b/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs new file mode 100644 index 0000000..fbabbb0 --- /dev/null +++ b/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs @@ -0,0 +1,12 @@ +using System; +using TNode.Attribute; +using UnityEngine.UIElements; + +namespace TNode.Editor.Inspector.InspectorImplementation{ + [NodeComponent] + public class FloatFieldItem:InspectorItem{ + public FloatFieldItem():base(){ + CreateBindable(new FloatField()); + } + } +} \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs.meta b/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs.meta new file mode 100644 index 0000000..170c2d3 --- /dev/null +++ b/TNode/Editor/Inspector/InspectorImplementation/FloatFieldItem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 932de5e7a487475aa764dd819cc33aa0 +timeCreated: 1656583186 \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/StringFieldItem.cs b/TNode/Editor/Inspector/InspectorImplementation/StringFieldItem.cs new file mode 100644 index 0000000..65d9ed9 --- /dev/null +++ b/TNode/Editor/Inspector/InspectorImplementation/StringFieldItem.cs @@ -0,0 +1,14 @@ +using TNode.Attribute; +using UnityEngine.UIElements; + +namespace TNode.Editor.Inspector.InspectorImplementation{ + /// + /// Force these element to bind native c# property + /// + [NodeComponent] + public class StringFieldItem:InspectorItem{ + public StringFieldItem():base(){ + CreateBindable(new TextField()); + } + } +} \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/DefaultInspectorItem.cs.meta b/TNode/Editor/Inspector/InspectorImplementation/StringFieldItem.cs.meta similarity index 100% rename from TNode/Editor/Inspector/InspectorImplementation/DefaultInspectorItem.cs.meta rename to TNode/Editor/Inspector/InspectorImplementation/StringFieldItem.cs.meta diff --git a/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs b/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs new file mode 100644 index 0000000..ce0e540 --- /dev/null +++ b/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs @@ -0,0 +1,12 @@ +using TNode.Attribute; +using UnityEngine.UIElements; + +namespace TNode.Editor.Inspector.InspectorImplementation{ + [NodeComponent] + public class ToggleFieldItem:InspectorItem{ + public ToggleFieldItem(){ + CreateBindable(new Toggle()); + } + + } +} \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs.meta b/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs.meta new file mode 100644 index 0000000..7ab36a8 --- /dev/null +++ b/TNode/Editor/Inspector/InspectorImplementation/ToggleFieldItem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d009d4819d604971976932b1d8f40bad +timeCreated: 1656580623 \ No newline at end of file diff --git a/TNode/Editor/Inspector/InspectorItem.cs b/TNode/Editor/Inspector/InspectorItem.cs index 49f2898..4d46d0e 100644 --- a/TNode/Editor/Inspector/InspectorItem.cs +++ b/TNode/Editor/Inspector/InspectorItem.cs @@ -7,6 +7,7 @@ namespace TNode.Editor.Inspector{ public abstract class InspectorItem:VisualElement,INodeDataBinding{ private NodeData _bindingNodeData; private string _bindingFieldName; + protected BaseField Bindable; protected event System.Action OnValueChanged; public string BindingPath{ @@ -46,13 +47,40 @@ namespace TNode.Editor.Inspector{ } protected T Value => GetValue(); + + protected void SetValue(T value){ + var fieldInfo = _bindingNodeData.GetType().GetField(BindingPath, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + Debug.Log(fieldInfo); + Debug.Log(fieldInfo?.FieldType ); + if (fieldInfo != null && fieldInfo.FieldType == typeof(T)){ + fieldInfo.SetValue(_bindingNodeData,value); + + //if value changed ,notify node inspector's current inspecting node view + if (parent.parent is NodeInspector nodeInspector){ + nodeInspector.NodeView.OnDataModified(); + } + } + else{ + Debug.LogError("Wrong Type for current node data"); + } + } public InspectorItem(){ OnValueChanged+= OnValueChangedHandler; } - + public void CreateBindable(BaseField bindable){ + Bindable = bindable; + this.Add(Bindable); + Bindable?.RegisterValueChangedCallback(e => { + SetValue(e.newValue); + }); + } private void OnValueChangedHandler(){ - + Bindable = this.Q>(); + if(Bindable!= null){ + Bindable.value = Value; + Bindable.label = BindingPath; + } } ~InspectorItem(){ OnValueChanged-= OnValueChangedHandler; diff --git a/TNode/Editor/Inspector/InspectorItemFactory.cs b/TNode/Editor/Inspector/InspectorItemFactory.cs new file mode 100644 index 0000000..411202c --- /dev/null +++ b/TNode/Editor/Inspector/InspectorItemFactory.cs @@ -0,0 +1,25 @@ +using System; +using TNode.Cache; +using TNode.Editor.Inspector.InspectorImplementation; +using Unity.VisualScripting; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace TNode.Editor.Inspector{ + public class InspectorItemFactory{ + + public InspectorItem Create(){ + //Check type of GraphDataType + var hasSpecificType = NodeEditorExtensions.HasSpecificType>(); + Debug.Log(typeof(InspectorItem)); + Debug.Log(hasSpecificType); + if (hasSpecificType){ + return NodeEditorExtensions.CreateInstance>(); + } + return null; + } + + } +} + \ No newline at end of file diff --git a/TNode/Editor/Inspector/DefaultInspectorItemFactory.cs.meta b/TNode/Editor/Inspector/InspectorItemFactory.cs.meta similarity index 100% rename from TNode/Editor/Inspector/DefaultInspectorItemFactory.cs.meta rename to TNode/Editor/Inspector/InspectorItemFactory.cs.meta diff --git a/TNode/Editor/Inspector/NodeInspector.cs b/TNode/Editor/Inspector/NodeInspector.cs index 63658fe..ca101db 100644 --- a/TNode/Editor/Inspector/NodeInspector.cs +++ b/TNode/Editor/Inspector/NodeInspector.cs @@ -1,14 +1,19 @@ -using System.Reflection; +using System; +using System.Collections.Generic; +using System.Reflection; using TNode.BaseViews; using TNode.Editor.BaseViews; using TNode.Models; +using Unity.VisualScripting; +using UnityEditor; +using UnityEditor.Experimental.GraphView; +using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; namespace TNode.Editor.Inspector{ public class NodeInspector:SimpleGraphSubWindow{ private NodeData _data; - public NodeData Data{ get => _data; set{ @@ -26,6 +31,7 @@ namespace TNode.Editor.Inspector{ } } public NodeInspector(){ + this.capabilities |= Capabilities.Resizable; style.position = new StyleEnum(Position.Absolute); var visualTreeAsset = Resources.Load("NodeInspector"); Debug.Log(visualTreeAsset); @@ -33,20 +39,21 @@ namespace TNode.Editor.Inspector{ BuildWindow(visualTreeAsset); } + private void RefreshInspector(){ //iterate field of data and get name of every fields,create a new inspector item of appropriate type and add it to the inspector for each field var body = this.Q("InspectorBody"); body.Clear(); - body.Add(new Label(_data.nodeName)); + body.StretchToParentSize(); foreach (var field in _data.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public)){ var bindingPath = field.Name; var type = field.FieldType; - DefaultInspectorItemFactory defaultInspectorItemFactory = new DefaultInspectorItemFactory(); + InspectorItemFactory inspectorItemFactory = new InspectorItemFactory(); //Invoke generic function Create<> of default inspector item factory to create an inspector item of appropriate type by reflection - MethodInfo methodInfo = defaultInspectorItemFactory.GetType().GetMethod("Create", BindingFlags.Instance | BindingFlags.Public); + MethodInfo methodInfo = inspectorItemFactory.GetType().GetMethod("Create", BindingFlags.Instance | BindingFlags.Public); if (methodInfo != null){ var genericMethod = methodInfo.MakeGenericMethod(type); - var createdItem = genericMethod.Invoke(defaultInspectorItemFactory,null) as VisualElement; + var createdItem = genericMethod.Invoke(inspectorItemFactory,null) as VisualElement; body.Add(createdItem); if (createdItem is INodeDataBindingBase castedItem){ @@ -55,8 +62,6 @@ namespace TNode.Editor.Inspector{ } } } - } - } } \ No newline at end of file diff --git a/TNode/Editor/Inspector/NodeInspectorInNode.cs b/TNode/Editor/Inspector/NodeInspectorInNode.cs new file mode 100644 index 0000000..15770de --- /dev/null +++ b/TNode/Editor/Inspector/NodeInspectorInNode.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using TNode.Attribute; +using TNode.Models; +using UnityEngine; +using UnityEngine.UIElements; + +namespace TNode.Editor.Inspector{ + public class NodeInspectorInNode:VisualElement{ + private NodeData _data; + public NodeData Data{ + get => _data; + set{ + _data = value; + UpdateData(); + + } + } + + private void UpdateData(){ + Debug.Log(_data); + if (_data != null){ + RefreshInspector(); + } + } + + private void RefreshInspector(){ + Clear(); + Debug.Log("In Node node inspector refresh for new data " + _data); + InspectorItemFactory inspectorItemFactory = new InspectorItemFactory(); + foreach (var field in _data.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public)){ + var bindingPath = field.Name; + var type = field.FieldType; + //check if the field has ShowInNodeView attribute + var showInNodeViewAttribute = field.GetCustomAttribute()!=null; + if(!showInNodeViewAttribute) + continue; + //Invoke generic function Create<> of default inspector item factory to create an inspector item of appropriate type by reflection + MethodInfo methodInfo = inspectorItemFactory.GetType().GetMethod("Create", BindingFlags.Instance | BindingFlags.Public); + if (methodInfo != null){ + var genericMethod = methodInfo.MakeGenericMethod(type); + var createdItem = genericMethod.Invoke(inspectorItemFactory,null) as VisualElement; + Add(createdItem); + if (createdItem is INodeDataBindingBase castedItem){ + castedItem.BindingNodeData = _data; + castedItem.BindingPath = bindingPath; + } + } + } + } + } +} \ No newline at end of file diff --git a/TNode/Editor/Inspector/NodeInspectorInNode.cs.meta b/TNode/Editor/Inspector/NodeInspectorInNode.cs.meta new file mode 100644 index 0000000..e96818d --- /dev/null +++ b/TNode/Editor/Inspector/NodeInspectorInNode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0ab3baa9312e4beaa12e5f2131f09969 +timeCreated: 1656584377 \ No newline at end of file diff --git a/TNode/Editor/Resources/NodeInspector.uxml b/TNode/Editor/Resources/NodeInspector.uxml index 1d0f297..70d1bab 100644 --- a/TNode/Editor/Resources/NodeInspector.uxml +++ b/TNode/Editor/Resources/NodeInspector.uxml @@ -1,3 +1,3 @@ - - + + diff --git a/TNode/Editor/SearchWindowProvider.cs b/TNode/Editor/SearchWindowProvider.cs index 1da8832..8a273bd 100644 --- a/TNode/Editor/SearchWindowProvider.cs +++ b/TNode/Editor/SearchWindowProvider.cs @@ -47,8 +47,7 @@ namespace TNode.Editor{ //Check if type is derived from NodeData if (typeof(NodeData).IsAssignableFrom(type)){ //Make an instance of the type - var nodeData = CreateInstance(type) as NodeData; - if (nodeData != null){ + if (Activator.CreateInstance(type) is NodeData nodeData){ nodeData.nodeName = "New Node"; ((IDataGraphView) _graphView).AddTNode(nodeData, new Rect(localPos.x, localPos.y, 100, 100)); } diff --git a/TNode/Models/NodeData.cs b/TNode/Models/NodeData.cs index c6f8a34..c89b557 100644 --- a/TNode/Models/NodeData.cs +++ b/TNode/Models/NodeData.cs @@ -1,4 +1,5 @@ using System; +using TNode.Attribute; using TNode.BaseModels; using UnityEngine; @@ -11,12 +12,13 @@ namespace TNode.Models{ /// /// [Serializable] - public class NodeData:ScriptableObject,IModel{ + public class NodeData:IModel{ public NodeData() : base(){ //Object Registration } public string nodeName; + [ShowInNodeView] public bool entryPoint; // #if UNITY_EDITOR // public Rect rect;