Skip to content

Commit ebc8bae

Browse files
committed
Editable Input Fields
Added shader graph kinda serializefield support. Added an example node.
1 parent cf70593 commit ebc8bae

4 files changed

Lines changed: 239 additions & 22 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
using GraphProcessor;
5+
using System.Linq;
6+
7+
[System.Serializable, NodeMenuItem("Custom/Serialized Field Test")]
8+
public class SerializedFieldTestNode : BaseNode
9+
{
10+
11+
[Input(name = "Vector 4"), SerializeField]
12+
public Vector4 vector4;
13+
14+
[Input(name = "Vector 3"), SerializeField]
15+
public Vector3 vector3;
16+
17+
[Input(name = "Vector 2"), SerializeField]
18+
public Vector2 vector2;
19+
20+
[Input(name = "Float"), SerializeField]
21+
public float floatInput;
22+
23+
[Input(name = "Int"), SerializeField]
24+
public int intInput;
25+
26+
[Input(name = "Empty")]
27+
public int intInput2;
28+
29+
[Input(name = "String"), SerializeField]
30+
public string stringInput;
31+
32+
[Input(name = "Color"), SerializeField]
33+
public Color color;
34+
35+
[Input(name = "Game Object"), SerializeField]
36+
public GameObject gameObject;
37+
38+
[Input(name = "Animation Curve"), SerializeField]
39+
public AnimationCurve animationCurve;
40+
41+
[Input(name = "Rigidbody"), SerializeField]
42+
public Rigidbody rigidbody;
43+
44+
public override string name => "Serialized Field Test";
45+
46+
protected override void Process() {}
47+
}

Assets/Examples/DefaultNodes/Nodes/SerializedFieldTestNode.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/com.alelievr.NodeGraphProcessor/Editor/Resources/GraphProcessorStyles/BaseNodeView.uss

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#settings-button > #icon {
1818
-unity-background-scale-mode: scale-to-fit;
19-
background-image : resource("Icons/SettingsIcons");
19+
background-image: resource("Icons/SettingsIcons");
2020
flex-grow: 1;
2121
}
2222

@@ -29,13 +29,11 @@
2929
#contents #top .true #connector, #contents #top .true #connector #cap,
3030
#contents #top .false #connector, #contents #top .false #connector #cap,
3131
#contents #top .loopBody #connector, #contents #top .loopBody #connector #cap,
32-
#contents #top .loopCompleted #connector, #contents #top .loopCompleted #connector #cap,
33-
{
32+
#contents #top .loopCompleted #connector, #contents #top .loopCompleted #connector #cap {
3433
border-radius: 0px;
3534
}
3635

37-
.Highlight
38-
{
36+
.Highlight {
3937
background-color: rgba(0, 63, 63, 0.8);
4038
}
4139

@@ -93,9 +91,126 @@ ParameterNodeView #controls EnumField > VisualElement > VisualElement {
9391
margin-right: -2px;
9492
}
9593

96-
#RightTitleContainer
97-
{
94+
#RightTitleContainer {
9895
justify-content: flex-end;
9996
flex-grow: 1;
10097
flex-direction: row;
101-
}
98+
}
99+
100+
#input-container {
101+
position: absolute;
102+
right: 100%;
103+
top: 45px;
104+
align-items: flex-end;
105+
--layer: -50;
106+
}
107+
108+
#input-container > .port-input-element > IntegerField,
109+
#input-container > .port-input-element > FloatField {
110+
min-width: 30px;
111+
max-width: 100px;
112+
}
113+
114+
#input-container > .port-input-element > ColorField,
115+
#input-container > .port-input-element > ObjectField,
116+
#input-container > .port-input-element > CurveField {
117+
margin-top: 0;
118+
margin-bottom: 0;
119+
}
120+
121+
#input-container > .port-input-element > TextField {
122+
min-width: 50px;
123+
max-width: 150px;
124+
}
125+
126+
#input-container > .port-input-element > CurveField {
127+
width: 100px;
128+
}
129+
130+
#input-container > .port-input-element > Vector4Field Label,
131+
#input-container > .port-input-element > Vector3Field Label,
132+
#input-container > .port-input-element > Vector2Field Label {
133+
font-size: 8px;
134+
min-width: 8px;
135+
flex-basis: 8px;
136+
padding-top: 2px;
137+
margin-right: 1px;
138+
}
139+
140+
#input-container > .port-input-element > Vector4Field FloatInput,
141+
#input-container > .port-input-element > Vector3Field FloatInput,
142+
#input-container > .port-input-element > Vector2Field FloatInput {
143+
min-width: 28px;
144+
}
145+
146+
#input-container > .port-input-element > Vector2Field .unity-composite-field__field-spacer {
147+
flex-grow: 0.01;
148+
}
149+
150+
#input-container ObjectFieldSelector {
151+
width: 17px;
152+
height: 15px;
153+
border-top-right-radius: 2px;
154+
border-bottom-right-radius: 2px;
155+
}
156+
157+
#input-container ObjectFieldDisplay > Label {
158+
margin-top: 2px;
159+
}
160+
161+
#input-container ObjectFieldDisplay > Image {
162+
margin: 0;
163+
}
164+
165+
#input-container > .port-input-element > ColorField {
166+
width: 60px;
167+
}
168+
169+
#input-container > .port-input-element > * {
170+
margin-left: 2px;
171+
}
172+
173+
#input-container > .port-input-element * {
174+
font-size: 8px;
175+
}
176+
177+
#input-container > .port-input-element {
178+
max-width: 180px;
179+
background-color: rgba(72, 72, 72, 0.6);
180+
margin-top: 3px;
181+
margin-bottom: 2px;
182+
padding-top: 1px;
183+
padding-bottom: 1px;
184+
border-top-left-radius: 4px;
185+
border-bottom-left-radius: 4px;
186+
height: 19px;
187+
overflow: hidden;
188+
padding-right: 1px;
189+
}
190+
191+
#input-container > .port-input-element.empty {
192+
width: 0;
193+
}
194+
195+
.collapsed #input-container {
196+
visibility: hidden;
197+
}
198+
199+
#input-container {
200+
opacity: 0.6;
201+
}
202+
203+
.node:checked > #input-container {
204+
opacity: 1;
205+
}
206+
207+
#input-container > .port-input-element IntegerInput,
208+
#input-container > .port-input-element FloatInput,
209+
#input-container > .port-input-element TextInput {
210+
border-top-left-radius: 1px;
211+
border-bottom-left-radius: 1px;
212+
border-top-right-radius: 1px;
213+
border-bottom-right-radius: 1px;
214+
padding-bottom: 1px;
215+
height: 15px;
216+
}

Assets/com.alelievr.NodeGraphProcessor/Editor/Views/BaseNodeView.cs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class BaseNodeView : NodeView
2828
public VisualElement controlsContainer;
2929
protected VisualElement debugContainer;
3030
protected VisualElement rightTitleContainer;
31+
private VisualElement inputContainerElement;
3132

3233
VisualElement settings;
3334
NodeSettingsView settingsContainer;
@@ -537,32 +538,55 @@ void ComputeOrderUpdatedCallback()
537538
Dictionary<string, VisualElement> hideElementIfConnected = new Dictionary<string, VisualElement>();
538539
Dictionary<FieldInfo, List<VisualElement>> fieldControlsMap = new Dictionary<FieldInfo, List<VisualElement>>();
539540

541+
protected void AddInputContainer()
542+
{
543+
inputContainerElement = new VisualElement {name = "input-container"};
544+
mainContainer.parent.Add(inputContainerElement);
545+
inputContainerElement.SendToBack();
546+
inputContainerElement.pickingMode = PickingMode.Ignore;
547+
}
548+
540549
protected virtual void DrawDefaultInspector(bool fromInspector = false)
541550
{
551+
AddInputContainer();
542552
var fields = nodeTarget.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
543553

544554
foreach (var field in fields)
545555
{
546556
//skip if the field is not serializable
547-
if (!field.IsPublic && field.GetCustomAttribute(typeof(SerializeField)) == null)
548-
continue ;
557+
if(!field.IsPublic && field.GetCustomAttribute(typeof(SerializeField)) == null)
558+
{
559+
AddEmptyField(field);
560+
continue;
561+
}
549562

550563
//skip if the field is an input/output and not marked as SerializedField
551-
bool hasInputAttribute = field.GetCustomAttribute(typeof(InputAttribute)) != null;
564+
bool hasInputAttribute = field.GetCustomAttribute(typeof(InputAttribute)) != null;
552565
bool hasInputOrOutputAttribute = hasInputAttribute || field.GetCustomAttribute(typeof(OutputAttribute)) != null;
553566
if (field.GetCustomAttribute(typeof(SerializeField)) == null && hasInputOrOutputAttribute)
554-
continue ;
567+
{
568+
AddEmptyField(field);
569+
continue;
570+
}
555571

556-
//skip if marked with NonSerialized or HideInInspector
557-
if (field.GetCustomAttribute(typeof(System.NonSerializedAttribute)) != null || field.GetCustomAttribute(typeof(HideInInspector)) != null)
558-
continue ;
572+
//skip if marked with NonSerialized or HideInInspector
573+
if (field.GetCustomAttribute(typeof(System.NonSerializedAttribute)) != null || field.GetCustomAttribute(typeof(HideInInspector)) != null)
574+
{
575+
AddEmptyField(field);
576+
continue;
577+
}
559578

560579
// Hide the field if we want to display in in the inspector
561580
var showInInspector = field.GetCustomAttribute<ShowInInspector>();
562581
if (showInInspector != null && !showInInspector.showInNode && !fromInspector)
582+
{
583+
AddEmptyField(field);
563584
continue;
585+
}
564586

565-
var elem = AddControlField(field, ObjectNames.NicifyVariableName(field.Name));
587+
var isSerializedInput = field.GetCustomAttribute(typeof(InputAttribute)) != null && field.GetCustomAttribute(typeof(SerializeField)) != null;
588+
589+
var elem = AddControlField(field, ObjectNames.NicifyVariableName(field.Name), isSerializedInput);
566590
if (hasInputAttribute)
567591
{
568592
hideElementIfConnected[field.Name] = elem;
@@ -575,6 +599,16 @@ protected virtual void DrawDefaultInspector(bool fromInspector = false)
575599
}
576600
}
577601

602+
private void AddEmptyField(FieldInfo field)
603+
{
604+
if(field.GetCustomAttribute(typeof(InputAttribute)) == null) return;
605+
606+
var box = new VisualElement {name = field.Name};
607+
box.AddToClassList("port-input-element");
608+
box.AddToClassList("empty");
609+
inputContainerElement.Add(box);
610+
}
611+
578612
void UpdateFieldVisibility(string fieldName, object newValue)
579613
{
580614
if (visibleConditions.TryGetValue(fieldName, out var list))
@@ -605,10 +639,10 @@ void UpdateOtherFieldValue(FieldInfo info, object newValue)
605639
genericUpdate.Invoke(this, new object[]{info, newValue});
606640
}
607641

608-
protected VisualElement AddControlField(string fieldName, string label = null, Action valueChangedCallback = null)
609-
=> AddControlField(nodeTarget.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), label, valueChangedCallback);
642+
protected VisualElement AddControlField(string fieldName, string label = null, bool isSerializedInput = false, Action valueChangedCallback = null)
643+
=> AddControlField(nodeTarget.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), label, isSerializedInput, valueChangedCallback);
610644

611-
protected VisualElement AddControlField(FieldInfo field, string label = null, Action valueChangedCallback = null)
645+
protected VisualElement AddControlField(FieldInfo field, string label = null, bool isSerializedInput = false, Action valueChangedCallback = null)
612646
{
613647
if (field == null)
614648
return null;
@@ -622,14 +656,26 @@ protected VisualElement AddControlField(FieldInfo field, string label = null, Ac
622656
// When you have the node inspector, it's possible to have multiple input fields pointing to the same
623657
// property. We need to update those manually otherwise they still have the old value in the inspector.
624658
UpdateOtherFieldValue(field, newValue);
625-
}, label);
659+
}, isSerializedInput ? "" : label);
626660

627661
if (!fieldControlsMap.TryGetValue(field, out var inputFieldList))
628662
inputFieldList = fieldControlsMap[field] = new List<VisualElement>();
629663
inputFieldList.Add(element);
630664

631-
if (element != null)
632-
controlsContainer.Add(element);
665+
if(element != null)
666+
{
667+
if(isSerializedInput)
668+
{
669+
var box = new VisualElement {name = field.Name};
670+
box.AddToClassList("port-input-element");
671+
box.Add(element);
672+
inputContainerElement.Add(box);
673+
}
674+
else
675+
{
676+
controlsContainer.Add(element);
677+
}
678+
}
633679

634680
var visibleCondition = field.GetCustomAttribute(typeof(VisibleIf)) as VisibleIf;
635681
if (visibleCondition != null)
@@ -659,6 +705,9 @@ void UpdateFieldValues()
659705

660706
internal void OnPortConnected(PortView port)
661707
{
708+
if(port.direction == Direction.Input && inputContainerElement?.Q(port.fieldName) != null)
709+
inputContainerElement.Q(port.fieldName).AddToClassList("empty");
710+
662711
if (hideElementIfConnected.TryGetValue(port.fieldName, out var elem))
663712
elem.style.display = DisplayStyle.None;
664713

@@ -667,6 +716,9 @@ internal void OnPortConnected(PortView port)
667716

668717
internal void OnPortDisconnected(PortView port)
669718
{
719+
if(port.direction == Direction.Input && inputContainerElement?.Q(port.fieldName) != null)
720+
inputContainerElement.Q(port.fieldName).RemoveFromClassList("empty");
721+
670722
if (hideElementIfConnected.TryGetValue(port.fieldName, out var elem))
671723
elem.style.display = DisplayStyle.Flex;
672724

0 commit comments

Comments
 (0)