Added Unity project files

This commit is contained in:
2026-06-07 16:58:24 +01:00
parent 3cc05d260b
commit 23bbcab156
3942 changed files with 453676 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e809894c9789ed4488974b1f99d92640
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System;
namespace VRC.Udon.Editor.ProgramSources.Attributes
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class UdonProgramSourceNewMenuAttribute : Attribute
{
public Type Type { get; }
public string DisplayName { get; }
public UdonProgramSourceNewMenuAttribute(Type type, string displayName)
{
Type = type;
DisplayName = displayName;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e5ced9511d591140b191bbd9e948e61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEditor;
using VRC.Udon.Common.Interfaces;
using VRC.Udon.ProgramSources;
using VRC.Udon.Serialization.OdinSerializer;
namespace VRC.Udon.Editor.ProgramSources
{
[CustomEditor(typeof(SerializedUdonProgramAsset))]
public class SerializedUdonProgramAssetEditor : UnityEditor.Editor
{
private SerializedProperty _serializedProgramBytesStringSerializedProperty;
private SerializedProperty _serializationDataFormatSerializedProperty;
private void OnEnable()
{
_serializedProgramBytesStringSerializedProperty = serializedObject.FindProperty("serializedProgramBytesString");
_serializationDataFormatSerializedProperty = serializedObject.FindProperty("serializationDataFormat");
}
public override void OnInspectorGUI()
{
DrawSerializationDebug();
}
[Conditional("UDON_DEBUG")]
private void DrawSerializationDebug()
{
EditorGUILayout.LabelField($"DataFormat: {(DataFormat)_serializationDataFormatSerializedProperty.enumValueIndex}");
if(string.IsNullOrEmpty(_serializedProgramBytesStringSerializedProperty.stringValue))
{
return;
}
if(_serializationDataFormatSerializedProperty.enumValueIndex == (int)DataFormat.JSON)
{
using(new EditorGUI.DisabledScope(true))
{
EditorGUILayout.TextArea(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(_serializedProgramBytesStringSerializedProperty.stringValue)));
}
}
else
{
using(new EditorGUI.DisabledScope(true))
{
SerializedUdonProgramAsset serializedUdonProgramAsset = (SerializedUdonProgramAsset)target;
IUdonProgram udonProgram = serializedUdonProgramAsset.RetrieveProgram();
byte[] serializedBytes = Serialization.OdinSerializer.SerializationUtility.SerializeValue(udonProgram, DataFormat.JSON, out List<UnityEngine.Object> _);
EditorGUILayout.TextArea(System.Text.Encoding.UTF8.GetString(serializedBytes));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1b5b45f24b268b42826fc5c5497dc15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 89cae0352490adc49b4d12ff7701437a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,98 @@
using System;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Editor.ProgramSources;
using VRC.Udon.Editor.ProgramSources.Attributes;
[assembly: UdonProgramSourceNewMenu(typeof(UdonAssemblyProgramAsset), "Udon Assembly Program Asset")]
namespace VRC.Udon.Editor.ProgramSources
{
[CreateAssetMenu(menuName = "VRChat/Udon/Udon Assembly Program Asset", fileName = "New Udon Assembly Program Asset")]
public class UdonAssemblyProgramAsset : UdonProgramAsset
{
[SerializeField]
protected string udonAssembly = "";
[SerializeField]
protected string assemblyError = null;
public string AssemblyError => assemblyError;
public delegate void AssembleDelegate(bool success, string assembly);
public event AssembleDelegate OnAssemble;
protected override void DrawProgramSourceGUI(UdonBehaviour udonBehaviour, ref bool dirty)
{
DrawAssemblyErrorTextArea();
DrawPublicVariables(udonBehaviour, ref dirty);
DrawAssemblyTextArea(!Application.isPlaying, ref dirty);
DrawProgramDisassembly();
}
protected override void RefreshProgramImpl()
{
AssembleProgram();
}
[PublicAPI]
protected virtual void DrawAssemblyTextArea(bool allowEditing, ref bool dirty)
{
EditorGUILayout.LabelField("Assembly Code", EditorStyles.boldLabel);
if(GUILayout.Button("Copy Assembly To Clipboard"))
{
EditorGUIUtility.systemCopyBuffer = udonAssembly;
}
EditorGUI.BeginChangeCheck();
using(new EditorGUI.DisabledScope(!allowEditing))
{
string newAssembly = EditorGUILayout.TextArea(udonAssembly);
if(EditorGUI.EndChangeCheck())
{
dirty = true;
Undo.RecordObject(this, "Edit Assembly Program Code");
udonAssembly = newAssembly;
UdonEditorManager.Instance.QueueAndRefreshProgram(this);
}
}
}
[PublicAPI]
protected void DrawAssemblyErrorTextArea()
{
if(string.IsNullOrEmpty(assemblyError))
{
return;
}
EditorGUILayout.LabelField("Assembly Error", EditorStyles.boldLabel);
using(new EditorGUI.DisabledScope(true))
{
EditorGUILayout.TextArea(assemblyError);
}
}
[PublicAPI]
protected void AssembleProgram()
{
try
{
program = UdonEditorManager.Instance.Assemble(udonAssembly);
assemblyError = null;
OnAssemble?.Invoke(true, udonAssembly);
}
catch(Exception e)
{
program = null;
assemblyError = e.Message;
Debug.LogException(e);
OnAssemble?.Invoke(false, assemblyError);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22203902d63dec94194fefc3e155c43b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using UnityEditor;
namespace VRC.Udon.Editor.ProgramSources
{
[CustomEditor(typeof(UdonAssemblyProgramAsset))]
public class UdonAssemblyProgramAssetEditor : UdonProgramAssetEditor
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3df823f3ab561fc43bcb81286e14b91d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System.IO;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources
{
[UnityEditor.AssetImporters.ScriptedImporter(1, "uasm")]
[UsedImplicitly]
public class UdonAssemblyProgramAssetImporter : UnityEditor.AssetImporters.ScriptedImporter
{
public override void OnImportAsset(UnityEditor.AssetImporters.AssetImportContext ctx)
{
UdonAssemblyProgramAsset udonAssemblyProgramAsset = ScriptableObject.CreateInstance<UdonAssemblyProgramAsset>();
SerializedObject serializedUdonAssemblyProgramAsset = new SerializedObject(udonAssemblyProgramAsset);
SerializedProperty udonAssemblyProperty = serializedUdonAssemblyProgramAsset.FindProperty("udonAssembly");
udonAssemblyProperty.stringValue = File.ReadAllText(ctx.assetPath);
serializedUdonAssemblyProgramAsset.ApplyModifiedProperties();
udonAssemblyProgramAsset.RefreshProgram();
ctx.AddObjectToAsset("Imported Udon Assembly Program", udonAssemblyProgramAsset);
ctx.SetMainObject(udonAssemblyProgramAsset);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c0638314c289c24193b47d1c53c9fca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7e9bedaf686c5e448b06e64cdfe95728
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2f2c773c057bbd749ad8fd66c018541a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0261d54fb1cc86b46a074b8d6e477cf0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0bea6600b38aa0249b02e3d3ee8a7d44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class ByteField : BaseField<byte>
{
public ByteField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToByte(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c66f97d5a5a38cb478fd4df11ece7be7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class CharField : BaseField<char>
{
public CharField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
TextField field = new TextField
{
maxLength = 1
};
field.RegisterValueChangedCallback(
e =>
value = e.newValue.ToCharArray()[0]);
Add(field);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 602ffcf431b3e4f41a18bd868751439a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class DecimalField : BaseField<decimal>
{
public DecimalField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
DoubleField field = new DoubleField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToDecimal(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e79144dad56140a7bcd0d9f945153784
timeCreated: 1632419615

View File

@ -0,0 +1,24 @@
using UnityEngine.UIElements;
using UIElements = UnityEditor.UIElements;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class LayerMaskField : BaseField<LayerMask>
{
public LayerMaskField() : base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create LayerMask Editor and listen for changes
UIElements.LayerMaskField field = new UIElements.LayerMaskField();
field.RegisterValueChangedCallback(e =>
{
this.value = e.newValue;
});
Add(field);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42d9183d7c7ce67448d1e010456e36f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using VRC.SDK3.Components;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class MirrorReflectionClearFlagsField : BaseField<MirrorClearFlags>
{
public MirrorReflectionClearFlagsField() : base(null, null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create LayerMask Editor and listen for changes
EnumField field = new EnumField();
field.Init(MirrorClearFlags.FromReferenceCamera);
field.RegisterValueChangedCallback(e =>
{
this.value = (MirrorClearFlags)e.newValue;
});
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2a6b99e05a39452082bd89b0feec45ca
timeCreated: 1660169942

View File

@ -0,0 +1,24 @@
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class QuaternionField : BaseField<Quaternion>
{
public QuaternionField() :base(null, null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Vector4 Editor and listen for changes
Vector4Field field = new Vector4Field();
field.RegisterValueChangedCallback(
e =>
value = new Quaternion(e.newValue.x, e.newValue.y, e.newValue.z, e.newValue.w)
);
Add(field);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c96407d2b7698c4c8a0476efa2c765d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class SByteField : BaseField<sbyte>
{
public SByteField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToSByte(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 42747856e1884be6b1112c8838963662
timeCreated: 1632419007

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class ShortField : BaseField<short>
{
public ShortField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToInt16(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcda1561abdb40c69f9eeb9211bd3e3d
timeCreated: 1632419462

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class UnsignedIntegerField : BaseField<uint>
{
public UnsignedIntegerField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToUInt32(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b91b14ff1e24276863f173d0e9c760c
timeCreated: 1632419145

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class UnsignedLongField : BaseField<ulong>
{
public UnsignedLongField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToUInt64(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d1c8af690a94ef0a670e5d321733414
timeCreated: 1632419341

View File

@ -0,0 +1,23 @@
using System;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class UnsignedShortField : BaseField<ushort>
{
public UnsignedShortField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Char Editor and listen for changes
IntegerField field = new IntegerField();
field.RegisterValueChangedCallback(
e =>
value = Convert.ToUInt16(e.newValue));
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1387a4616a8f4c87bd0d55d2ffc021c8
timeCreated: 1632419528

View File

@ -0,0 +1,23 @@
using UnityEngine.UIElements;
using System;
using VRC.SDKBase;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class VRCUrlField : BaseField<VRCUrl>
{
public VRCUrlField():base(null,null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create Text Editor and listen for changes
TextField field = new TextField(50, false, false, Char.MinValue);
field.RegisterValueChangedCallback(
e =>
value = new VRCUrl(e.newValue)
);
Add(field);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eeb751ae1c234a04291c5039626f3470
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using VRC.SDK3.Components.Video;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
{
public class VideoErrorField : BaseField<VideoError>
{
public VideoErrorField() : base(null, null)
{
// Set up styling
AddToClassList("UdonValueField");
// Create LayerMask Editor and listen for changes
EnumField field = new EnumField();
field.Init(VideoError.Unknown);
field.RegisterValueChangedCallback(e =>
{
this.value = (VideoError)e.newValue;
});
Add(field);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 22a713ed81ae4c7e9ad58760232ce573
timeCreated: 1660170466

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b029e079cc40515438800823a9649f5b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
#if UNITY_2019_3_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
using System;
using UnityEditor.SceneManagement;
using UnityEngine;
using VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView;
#if UNITY_2019_3_OR_NEWER
namespace UnityEditor.Experimental.GraphView
#else
namespace UnityEditor.Experimental.UIElements.GraphView
#endif
{
public static class GraphElementExtension
{
public static void Reload(this GraphElement element)
{
var evt = new Event()
{
type = EventType.ExecuteCommand,
commandName = UdonGraphCommands.Reload
};
using (var e = ExecuteCommandEvent.GetPooled(evt))
{
element.SendEvent(e);
}
}
public static void Compile(this GraphElement element)
{
var evt = new Event()
{
type = EventType.ExecuteCommand,
commandName = UdonGraphCommands.Compile
};
using (var e = ExecuteCommandEvent.GetPooled(evt))
{
element.SendEvent(e);
}
}
public static string GetUid(this GraphElement element)
{
#if UNITY_2019_3_OR_NEWER
return element.viewDataKey;
#else
return element.persistenceKey;
#endif
}
public static void MarkDirty()
{
if (!EditorApplication.isPlaying)
{
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
}
public static Vector2 GetSnappedPosition(Vector2 position)
{
// don't snap at 0 size
var snap = Settings.GridSnapSize;
if (snap == 0) return position;
position.x = (float)Math.Round(position.x / snap) * snap;
position.y = (float)Math.Round(position.y / snap) * snap;
return position;
}
public static Rect GetSnappedRect(Rect rect)
{
rect.position = GetSnappedPosition(rect.position);
return rect;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 469db50616185d04e8a46dcd75db12d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,86 @@
#if UNITY_2019_3_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.Experimental.UIElements;
#endif
using System;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonArrayEditor : VisualElement
{
private IArrayProvider _inspector;
private Button _editArrayButton;
private Action<object> _setValueCallback;
private Type _type;
private object _value;
private bool _inspectorOpen = false;
public UdonArrayEditor(Type t, Action<object> valueChangedAction, object value)
{
_setValueCallback = valueChangedAction;
_value = value;
_type = t.GetElementType();
_editArrayButton = new Button(EditArray)
{
text = "Edit",
name = "array-editor",
};
Add(_editArrayButton);
}
private void EditArray()
{
if (_inspector == null)
{
// Create it new
Type typedArrayInspector = (typeof(UdonArrayInspector<>)).MakeGenericType(_type);
_inspector = (Activator.CreateInstance(typedArrayInspector, null, _value) as IArrayProvider);
AddInspector();
_inspectorOpen = true;
_editArrayButton.text = "Save";
return;
}
else
{
// Update Values when 'Save' is clicked
if(_inspectorOpen)
{
// Update Values
var values = _inspector.GetValues();
_setValueCallback(values);
// Remove Inspector
_inspector.RemoveFromHierarchy();
// Update Button Text
_editArrayButton.text = "Edit";
_inspectorOpen = false;
return;
}
else
{
// Inspector exists, it's just removed
_inspectorOpen = true;
AddInspector();
_editArrayButton.text = "Save";
}
}
}
private void AddInspector()
{
if (parent.GetType() == typeof(UdonPort))
{
parent.parent.Add(_inspector as VisualElement);
}
else
{
Add(_inspector as VisualElement);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f257a6eeae213a4db991d486cace003
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
#if UNITY_2019_3_OR_NEWER
using UnityEngine.UIElements;
using UnityEditor.UIElements;
#else
using UnityEngine.Experimental.UIElements;
using UnityEditor.Experimental.UIElements;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonArrayInspector<T> : VisualElement, IArrayProvider
{
private ScrollView _scroller;
private List<VisualElement> _fields = new List<VisualElement>();
private IntegerField _sizeField;
private Action<object> _setValueCallback;
public UdonArrayInspector(Action<object> valueChangedAction, object value)
{
_setValueCallback = valueChangedAction;
AddToClassList("input-inspector");
var resizeContainer = new VisualElement()
{
name = "resize-container",
};
resizeContainer.Add(new Label("size"));
_sizeField = new IntegerField();
_sizeField.isDelayed = true;
#if UNITY_2019_3_OR_NEWER
_sizeField.RegisterValueChangedCallback((evt) =>
#else
_sizeField.OnValueChanged((evt) =>
#endif
{
ResizeTo(evt.newValue);
});
resizeContainer.Add(_sizeField);
Add(resizeContainer);
_scroller = new ScrollView()
{
name = "array-scroll",
};
Add(_scroller);
if (value == null)
{
// Can't show if array is empty
_sizeField.value = 0;
return;
}
else
{
IEnumerable<T> values = (value as IEnumerable<T>);
if (values == null)
{
Debug.LogError($"Couldn't convert {value} to {typeof(T).ToString()} Array");
return;
}
// Populate fields and their values from passed-in array
_fields = new List<VisualElement>();
foreach (var item in values)
{
var field = GetValueField(item);
_fields.Add(field);
_scroller.contentContainer.Add(field);
}
_sizeField.value = values.Count();
}
}
private void ResizeTo(int newValue)
{
_sizeField.value = newValue;
// Create from scratch if currentFields are null
if(_fields == null)
{
Debug.Log($"Creating from Scratch");
_fields = new List<VisualElement>();
for (int i = 0; i < newValue; i++)
{
var field = GetValueField(null);
_fields.Add(field);
_scroller.contentContainer.Add(field as VisualElement);
}
return;
}
// Shrink list if new value is less than old one
if(newValue < _fields.Count)
{
for (int i = _fields.Count - 1; i >= newValue; i--)
{
(_fields[i] as VisualElement).RemoveFromHierarchy();
_fields.RemoveAt(i);
}
MarkDirtyRepaint();
return;
}
// Expand list if new value is more than old one.
if(newValue > _fields.Count)
{
int numberToAdd = newValue - _fields.Count;
for (int i = 0; i < numberToAdd; i++)
{
var field = GetValueField(null);
if (field == null)
{
Debug.LogWarning($"Sorry, can't edit object of type {typeof(T).ToString()} yet.");
return;
}
_fields.Add(field);
_scroller.contentContainer.Add(field as VisualElement);
}
MarkDirtyRepaint();
return;
}
}
private VisualElement GetValueField(object value)
{
return UdonFieldFactory.CreateField(typeof(T), value, _setValueCallback);
}
public object GetValues()
{
var result = new List<T>();
for (int i = 0; i < _fields.Count; i++)
{
var f = (_fields[i] as INotifyValueChanged<T>);
result.Add(f.value);
}
return result.ToArray();
}
}
public interface IArrayProvider
{
object GetValues();
void RemoveFromHierarchy(); // in VisualElement
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4f0ade55ae13b6468a765826f1f2540
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,212 @@
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
using System;
using UnityEditor;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public sealed class UdonComment : UdonGraphElement, IUdonGraphElementDataProvider
{
private readonly VisualElement _mainContainer;
private readonly Label _label;
private readonly TextField _textField;
private readonly CustomData _customData = new CustomData();
private readonly UdonGraph _graph;
public UdonGroup group;
// Called from Context menu and Reload
public static UdonComment Create(string value, Rect position, UdonGraph graph)
{
var comment = new UdonComment("", graph);
// make sure rect size is not 0
position.width = position.width > 0 ? position.width : 128;
position.height = position.height > 0 ? position.height : 40;
comment._customData.layout = position;
comment._customData.title = value;
comment.UpdateFromData();
UdonGraph.MarkSceneDirty();
return comment;
}
public static UdonComment Create(UdonGraphElementData elementData, UdonGraph graph)
{
var comment = new UdonComment(elementData.jsonData, graph);
comment.UpdateFromData();
UdonGraph.MarkSceneDirty();
return comment;
}
private UdonComment(string jsonData, UdonGraph graph)
{
name = "comment";
_graph = graph;
capabilities |= Capabilities.Selectable | Capabilities.Movable | Capabilities.Deletable |
Capabilities.Resizable;
pickingMode = PickingMode.Ignore;
type = UdonGraphElementType.UdonComment;
if(!string.IsNullOrEmpty(jsonData))
{
EditorJsonUtility.FromJsonOverwrite(jsonData, _customData);
}
_mainContainer = new VisualElement();
_mainContainer.StretchToParentSize();
_mainContainer.AddToClassList("mainContainer");
Add(_mainContainer);
_label = new Label();
_label.RegisterCallback<MouseDownEvent>(OnLabelClick);
_mainContainer.Add(_label);
_textField = new TextField(1000, true, false, '*')
{
isDelayed = true
};
// Support IME
_textField.RegisterCallback<FocusInEvent>(evt =>{ Input.imeCompositionMode = IMECompositionMode.On;});
_textField.RegisterCallback<FocusOutEvent>(evt =>
{
SetText(_textField.text);
Input.imeCompositionMode = IMECompositionMode.Auto;
SwitchToEditMode(false);
});
_textField.RegisterValueChangedCallback(evt =>
{
SetText(evt.newValue);
SwitchToEditMode(false);
});
}
private void SaveNewData()
{
_graph.SaveGraphElementData(this);
}
private void UpdateFromData()
{
if(_customData != null)
{
layer = _customData.layer;
if(string.IsNullOrEmpty(_customData.uid))
{
_customData.uid = Guid.NewGuid().ToString();
}
uid = _customData.uid;
SetPosition(_customData.layout);
SetText(_customData.title);
}
}
protected override void OnCustomStyleResolved(ICustomStyle istyle)
{
base.OnCustomStyleResolved(istyle);
// Something is forcing style! Resetting a few things here.
style.borderBottomWidth = 1;
var resizer = this.Q(null, "resizer");
if (resizer == null) return;
resizer.style.paddingTop = 0;
resizer.style.paddingLeft = 0;
}
public override void SetPosition(Rect newPos)
{
newPos = GraphElementExtension.GetSnappedRect(newPos);
base.SetPosition(newPos);
}
public override void UpdatePresenterPosition()
{
base.UpdatePresenterPosition();
_customData.layout = GraphElementExtension.GetSnappedRect(GetPosition());
SaveNewData();
group?.SaveNewData();
}
private double lastClickTime;
private const double DOUBLE_CLICK_SPEED = 0.5;
private void OnLabelClick(MouseDownEvent evt)
{
var newTime = EditorApplication.timeSinceStartup;
if(newTime - lastClickTime < DOUBLE_CLICK_SPEED)
{
SwitchToEditMode(true);
}
lastClickTime = newTime;
}
private void SwitchToEditMode(bool switchingToEdit)
{
if (switchingToEdit)
{
_mainContainer.Remove(_label);
_textField.value = _label.text;
_mainContainer.Add(_textField);
_textField.delegatesFocus = true;
_textField.Focus();
}
else
{
_mainContainer.Remove(_textField);
_mainContainer.Add(_label);
}
MarkDirtyRepaint();
}
private void SetText(string value)
{
Undo.RecordObject(_graph.graphProgramAsset, "Rename Comment");
value = value.TrimEnd();
_customData.title = value;
_label.text = value;
SaveNewData();
MarkDirtyRepaint();
}
public UdonGraphElementData GetData()
{
return new UdonGraphElementData(UdonGraphElementType.UdonComment, uid,
EditorJsonUtility.ToJson(_customData));
}
// CustomData is serialized in user assets, so we can't rename/modify/remove any of these variables
// ReSharper disable InconsistentNaming
// ReSharper disable NotAccessedField.Local
// ReSharper disable FieldCanBeMadeReadOnly.Local
// ReSharper disable UnusedMember.Global
// ReSharper disable UnassignedField.Global
#pragma warning disable CS0649
private class CustomData
{
public string uid;
public Rect layout;
public string title = "Comment";
public int layer;
public Color elementTypeColor;
}
#pragma warning restore CS0649
// ReSharper enable UnassignedField.Global
// ReSharper enable UnusedMember.Global
// ReSharper enable InconsistentNaming
// ReSharper enable NotAccessedField.Local
// ReSharper enable FieldCanBeMadeReadOnly.Local
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e5916b8dd19e4445a9156a457b82ee4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,48 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
#else
using UnityEditor.Experimental.UIElements.GraphView;
#endif
using System;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGraphElement : GraphElement
{
#if UNITY_2019_3_OR_NEWER
public string uid { get => viewDataKey; set => viewDataKey = value; }
#else
public string uid { get => persistenceKey; set => persistenceKey = value; }
#endif
internal UdonGraphElementType type = UdonGraphElementType.GraphElement;
internal UdonGraphElement()
{
}
}
public interface IUdonGraphElementDataProvider
{
UdonGraphElementData GetData();
}
[Serializable]
public class GraphRect
{
public float x;
public float y;
public float width;
public float height;
public GraphRect(Rect input)
{
this.x = Mathf.Round(input.x);
this.y = Mathf.Round(input.y);
this.width = Mathf.Round(input.width);
this.height = Mathf.Round(input.height);
}
public Rect rect => new Rect(this.x, this.y, this.width, this.height);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba3ecc4c46929404d8c2ec920743b823
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,134 @@
using UnityEngine;
using UnityEngine.UIElements;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGraphEvents : VisualElement
{
private readonly UdonGraph _graph;
private UdonNodeData[] _events;
private readonly VisualElement _list;
private readonly Label _placeholder;
public UdonGraphEvents(UdonGraph graph, UdonSearchManager searchManager)
{
_graph = graph;
name = "UdonGraphEvents";
AddToClassList("baseBlock");
var header = new VisualElement();
header.AddToClassList("header");
header.RegisterCallback((MouseUpEvent e) =>
{
ToggleInClassList("collapsed");
});
Add(header);
var leftSide = new VisualElement();
header.Add(leftSide);
var collapseIcon = new VisualElement();
collapseIcon.AddToClassList("collapseIcon");
leftSide.Add(collapseIcon);
var headerText = new Label("Events");
leftSide.Add(headerText);
var eventAddBtn = new Button(() =>
{
var screenPosition = GUIUtility.GUIToScreenPoint(_graph.lastMousePosition);
searchManager.OpenEventSearch(screenPosition);
})
{
text = "+"
};
eventAddBtn.AddToClassList("addEventBtn");
header.Add(eventAddBtn);
_list = new VisualElement();
_list.AddToClassList("list");
Add(_list);
_placeholder = new Label("No Events Added");
_placeholder.AddToClassList("placeholder");
_list.Add(_placeholder);
}
public UdonNodeData[] Events
{
set
{
_events = value;
var oldEvents = this.Query<Button>(null, "udonEvent").ToList();
foreach (var e in oldEvents)
{
_list.Remove(e);
}
if (_events.Length == 0)
{
if (!_list.Contains(_placeholder))
{
_list.Add(_placeholder);
}
}
else
{
if (_list.Contains(_placeholder))
{
_placeholder.RemoveFromHierarchy();
}
}
foreach (var e in _events)
{
var eventButton = new Button(() =>
{
_graph.ClearSelection();
_graph.AddToSelection(_graph.GetNodeByGuid(e.uid));
_graph.FrameSelection();
})
{
name = $"jumpTo_{e.fullName}"
};
eventButton.AddToClassList("udonEvent");
var eventName = e.fullName.Replace("Event_", "");
if (eventName.StartsWithCached("Custom"))
{
var customName = e.nodeValues[0].Deserialize() as string;
if (!string.IsNullOrEmpty(customName))
{
eventButton.text = e.nodeValues[0].Deserialize() as string;
}
else
{
eventButton.text = "Custom Event";
}
// append arity
var parameters = e.nodeValues.Length - 2;
if (parameters > 0)
{
eventButton.text += $" ({parameters})";
}
eventButton.AddToClassList("customEvent");
_list.Insert(0, eventButton);
continue;
}
switch (eventName)
{
case "OnVariableChange":
eventButton.text = $"{_graph.GetVariableName(e.nodeValues[0].Deserialize() as string)} Change";
eventButton.AddToClassList("variableChange");
_list.Add(eventButton);
break;
default:
eventButton.text = eventName;
_list.Add(eventButton);
break;
}
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75df7c5698fa41118a907e0f229bb537
timeCreated: 1641352883

View File

@ -0,0 +1,148 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGraphGroups : VisualElement
{
private readonly VisualElement _list;
private readonly Label _placeholder;
private UdonGroup[] _groups;
public UdonGraphGroups(UdonGraph graph)
{
UdonGraph graph1 = graph;
name = "UdonGraphGroups";
AddToClassList("baseBlock");
var header = new VisualElement();
header.AddToClassList("header");
header.RegisterCallback((MouseUpEvent e) =>
{
ToggleInClassList("collapsed");
});
Add(header);
var leftSide = new VisualElement();
header.Add(leftSide);
var collapseIcon = new VisualElement();
collapseIcon.AddToClassList("collapseIcon");
leftSide.Add(collapseIcon);
var headerText = new Label("Groups");
leftSide.Add(headerText);
var eventAddBtn = new Button(() =>
{
var groupRect = graph1.GetRectFromMouse();
groupRect.x += 200;
UdonGroup group = UdonGroup.Create("Group", groupRect, graph1);
Undo.RecordObject(graph1.graphProgramAsset, "Add Group");
graph1.AddElement(group);
group.UpdateDataId();
foreach (ISelectable item in graph1.selection)
{
switch (item)
{
case UdonNode node:
group.AddElement(node);
break;
case UdonComment comment:
group.AddElement(comment);
break;
}
}
group.Initialize();
graph1.SaveGraphElementData(group);
AddGroup(group);
})
{
text = "+"
};
eventAddBtn.AddToClassList("addEventBtn");
header.Add(eventAddBtn);
_list = new VisualElement();
_list.AddToClassList("list");
Add(_list);
_placeholder = new Label("No Groups Added");
_placeholder.AddToClassList("placeholder");
_list.Add(_placeholder);
}
public void AddGroup(UdonGroup group)
{
if (Groups != null)
{
var newGroups = Groups.ToList();
newGroups.Add(group);
Groups = newGroups.ToArray();
}
else
{
Groups = new[] { group };
}
}
public new void Clear()
{
Groups = Array.Empty<UdonGroup>();
}
private UdonGroup[] Groups
{
get => _groups;
set
{
_groups = value;
UpdateGroups(value);
}
}
private void UpdateGroups(UdonGroup[] value)
{
var oldGroups = this.Query(null, "udonGroup").ToList();
foreach (var group in oldGroups)
{
_list.Remove(group);
}
if (value.Length == 0)
{
if (!_list.Contains(_placeholder))
{
_list.Add(_placeholder);
}
}
else
{
if (_list.Contains(_placeholder))
{
_placeholder.RemoveFromHierarchy();
}
}
foreach (var group in value)
{
group.UpdateGraphGroups = UpdateGroupsDelegate;
var groupBtn = new Button(() => { group.SelectGroup(); })
{
name = $"jumpTo_{group.name}",
text = group.title
};
groupBtn.AddToClassList("udonGroup");
_list.Add(groupBtn);
}
}
private void UpdateGroupsDelegate()
{
UpdateGroups(_groups);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2adbf86181214870b117bdc6c99946d8
timeCreated: 1641420287

View File

@ -0,0 +1,116 @@
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGraphToolbar: VisualElement
{
private readonly IntegerField _updateOrderIntField;
private readonly Button _graphHighlightFlow;
private readonly UdonGraphStatus _graphStatus;
private float _sidebarOffset = 238f;
private VisualElement _sidebarSpacer;
private readonly StyleSheet styles = (StyleSheet)Resources.Load("UdonToolbarStyle");
public UdonGraphToolbar(UdonGraph graphView)
{
name = "UdonGraphToolbar";
graphView.OnSidebarResize += OnSidebarResize;
if (!graphView.styleSheets.Contains(styles))
{
graphView.styleSheets.Add(styles);
}
bool hasValue = graphView.graphProgramAsset != null && graphView.graphProgramAsset.graphData != null;
_updateOrderIntField = new IntegerField
{
name = "UpdateOrderIntegerField",
label = "Update Order",
value = hasValue ? graphView.graphProgramAsset.graphData.updateOrder : 0
};
_updateOrderIntField.AddToClassList("updateOrderField");
_updateOrderIntField.RegisterValueChangedCallback(e =>
{
Undo.RecordObject(graphView.graphProgramAsset, "Changed UpdateOrder");
graphView.graphProgramAsset.graphData.updateOrder = e.newValue;
graphView.SaveGraphToDisk();
AssetDatabase.SaveAssets();
});
_updateOrderIntField.isDelayed = true;
var leftSide = new VisualElement();
_sidebarSpacer = new VisualElement();
_sidebarSpacer.AddToClassList("toolbarSpacer");
Add(_sidebarSpacer);
Add(leftSide);
leftSide.Add(_updateOrderIntField);
var rightSide = new VisualElement();
rightSide.AddToClassList("toolbarRightSide");
_graphHighlightFlow = new Button(() =>
{
Settings.HighlightFlow = !Settings.HighlightFlow;
_graphHighlightFlow?.EnableInClassList("selected", Settings.HighlightFlow);
graphView.OnHighlightFlowChanged();
})
{
text = "Highlight Flow",
tooltip = "Highlights the flow-connected nodes on click"
};
rightSide.Add(_graphHighlightFlow);
Button graphCompile = new Button(() =>
{
if (graphView.graphProgramAsset != null &&
graphView.graphProgramAsset is AbstractUdonProgramSource udonProgramSource)
{
UdonEditorManager.Instance.QueueAndRefreshProgram(udonProgramSource);
}
})
{ text = "Compile" };
rightSide.Add(graphCompile);
Button graphReload = new Button(graphView.Reload)
{ text = "Reload" };
rightSide.Add(graphReload);
_graphStatus = new UdonGraphStatus(graphView);
rightSide.Add(_graphStatus);
Add(rightSide);
UpdateStatusAsset(graphView.graphProgramAsset);
// block dragging/moving
RegisterCallback((EventCallback<DragUpdatedEvent>) (e => e.StopPropagation()));
RegisterCallback((EventCallback<WheelEvent>) (e => e.StopPropagation()));
RegisterCallback((EventCallback<MouseDownEvent>) (e => e.StopPropagation()));
}
private void UpdateStatusAsset(UdonGraphProgramAsset graph)
{
if (graph == null) return;
_graphStatus.LoadAsset(graph);
}
public void RefreshAsset(UdonGraphProgramAsset asset)
{
if (asset == null) return;
_updateOrderIntField.value = asset.graphData.updateOrder;
UpdateStatusAsset(asset);
}
private void OnSidebarResize(object sender, MouseMoveEvent evt)
{
_sidebarOffset = evt.mousePosition.x + 8f;
_sidebarSpacer.style.width = new StyleLength(_sidebarOffset);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1889c18ccf7e41c7af45075ff3ad65b6
timeCreated: 1641509003

View File

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGraphVariables : VisualElement, IUdonGraphElementDataProvider
{
private readonly CustomData _customData = new CustomData();
private readonly UdonGraph _graph;
private readonly VisualElement _list;
private readonly Label _placeholder;
private Button _addButton;
private readonly string _guid;
private Dictionary<string, UdonVariableRow> _idToRow;
private Action<VisualElement, string> EditTextRequested { get; set; }
public UdonGraphVariables(UdonGraph graph, UdonSearchManager searchManager, Action<VisualElement, string> editTextRequested)
{
EditTextRequested = editTextRequested;
_guid = GUID.Generate().ToString();
_graph = graph;
AddToClassList("baseBlock");
var header = new VisualElement();
header.AddToClassList("header");
Add(header);
header.RegisterCallback((MouseUpEvent e) =>
{
ToggleInClassList("collapsed");
});
var leftSide = new VisualElement();
header.Add(leftSide);
var collapseIcon = new VisualElement();
collapseIcon.AddToClassList("collapseIcon");
leftSide.Add(collapseIcon);
var headerText = new Label("Variables");
leftSide.Add(headerText);
var eventAddBtn = new Button(() =>
{
var screenPosition = GUIUtility.GUIToScreenPoint(_graph.lastMousePosition);
searchManager.OpenVariableSearch(screenPosition);
})
{
text = "+"
};
eventAddBtn.AddToClassList("addEventBtn");
header.Add(eventAddBtn);
_list = new VisualElement();
_list.AddToClassList("list");
Add(_list);
_placeholder = new Label("No Variables Added");
_placeholder.AddToClassList("placeholder");
_list.Add(_placeholder);
_idToRow = new Dictionary<string, UdonVariableRow>();
_list.RegisterCallback<KeyDownEvent>(_graph.OnKeyDown);
}
public void AddFromData(UdonNodeData nodeData)
{
// don't add internal variables, which start with __
// Todo: handle all "__" variables instead, need to tell community first and let the word spread
string newVariableName = (string)nodeData.nodeValues[(int)UdonParameterProperty.ValueIndices.Name].Deserialize();
if (newVariableName.StartsWithCached("__returnValue"))
{
return;
}
UdonNodeDefinition definition = UdonEditorManager.Instance.GetNodeDefinition(nodeData.fullName);
if (definition != null)
{
var row = new UdonVariableRow(new UdonParameterField(_graph, nodeData, EditTextRequested),
new UdonParameterProperty(definition, nodeData));
row.AddToClassList("udonVariable");
_list.Add(row);
_idToRow.Add(nodeData.uid, row);
}
if (_list.Contains(_placeholder))
{
_placeholder.RemoveFromHierarchy();
}
}
public void LoadData(UdonGraphElementData data)
{
JsonUtility.FromJsonOverwrite(data.jsonData, _customData);
visible = _customData.visible;
}
private void SaveData()
{
_graph.SaveGraphElementData(this);
}
public UdonGraphElementData GetData()
{
return new UdonGraphElementData(UdonGraphElementType.VariablesWindow, _guid, JsonUtility.ToJson(_customData));
}
public void RemoveByID(string id)
{
if (!_idToRow.TryGetValue(id, out UdonVariableRow row)) return;
_list.Remove(row);
_idToRow.Remove(id);
}
public new void Clear()
{
_idToRow?.Clear();
_list.Add(_placeholder);
var oldVars = this.Query<UdonVariableRow>(null, "udonVariable").ToList();
foreach (var oldVar in oldVars)
{
_list.Remove(oldVar);
}
}
[Serializable]
private class CustomData {
public bool visible = true;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 346f4d7e451c44daaaacbf470d40c254
timeCreated: 1641412295

View File

@ -0,0 +1,245 @@
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonGroup : Group, IUdonGraphElementDataProvider
{
public Action UpdateGraphGroups;
private string Uid { get; set; }
private readonly CustomData _customData = new CustomData();
private readonly UdonGraph _graph;
private const int GROUP_LAYER = -1;
public static UdonGroup Create(string value, Rect position, UdonGraph graph)
{
var group = new UdonGroup("", graph)
{
Uid = Guid.NewGuid().ToString()
};
// make sure rect size is not 0
position.width = position.width > 0 ? position.width : 128;
position.height = position.height > 0 ? position.height : 128;
group._customData.uid = group.Uid;
group._customData.layout = position;
group._customData.title = value;
group._customData.layer = GROUP_LAYER;
group._customData.elementTypeColor = Color.black;
return group;
}
// Called in Reload > RestoreElementFromData
public static UdonGroup Create(UdonGraphElementData elementData, UdonGraph graph)
{
return new UdonGroup(elementData.jsonData, graph);
}
// current order of operations issue when creating a group from the context menu means this isn't set until first save. This allows us to force it.
public void UpdateDataId()
{
_customData.uid = Uid;
}
// Build a Group from jsonData, save to userData
private UdonGroup(string jsonData, UdonGraph graph)
{
title = "Group";
_graph = graph;
layer = GROUP_LAYER;
if (!string.IsNullOrEmpty(jsonData))
{
EditorJsonUtility.FromJsonOverwrite(jsonData, _customData);
}
// listen for changes on child elements
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
}
public sealed override string title
{
get => base.title;
set
{
base.title = value;
UpdateGraphGroups?.Invoke();
}
}
private void OnGeometryChanged(GeometryChangedEvent evt)
{
_customData.layout = GraphElementExtension.GetSnappedRect(GetPosition());
}
public void Initialize()
{
if (_customData != null)
{
// Propagate data to useful places
title = _customData.title;
layer = _customData.layer;
if (string.IsNullOrEmpty(_customData.uid))
{
_customData.uid = Guid.NewGuid().ToString();
}
Uid = _customData.uid;
// Add all elements from graph to self
var childUIDs = _customData.containedElements;
if (childUIDs.Count > 0)
{
foreach (var item in childUIDs)
{
GraphElement element = _graph.GetElementByGuid(item);
if (element != null)
{
if (ContainsElement(element)) continue;
AddElement(element);
if (element is UdonComment c)
{
c.group = this;
}
else if (element is UdonNode n)
{
n.group = this;
}
}
}
}
else
{
// No children, so restore the saved position
SetPosition(_customData.layout);
}
}
}
public override void SetPosition(Rect newPos)
{
newPos = GraphElementExtension.GetSnappedRect(newPos);
base.SetPosition(newPos);
}
public void SaveNewData()
{
_graph.SaveGraphElementData(this);
}
// Save data to asset after new position set
public override void UpdatePresenterPosition()
{
base.UpdatePresenterPosition();
Rect snappedRect = GraphElementExtension.GetSnappedRect(GetPosition());
base.SetPosition(snappedRect);
SaveNewData();
}
public void SelectGroup()
{
_graph.ClearSelection();
// groups exist on a separate layer and fairly opaque from an actual graph view, so it doesn't report them in the `GetElementByGuid`
// so we force-select them via UI Elements ways
_graph.AddToSelection(_graph.contentViewContainer.Query<UdonGroup>().Where(g => g.Equals(this)).First());
var childUIDs = _customData.containedElements;
if (childUIDs.Count > 0)
{
foreach (var item in childUIDs)
{
_graph.AddToSelection( _graph.GetElementByGuid(item));
}
}
_graph.FrameSelection();
}
// Save data to asset after rename
protected override void OnGroupRenamed(string oldName, string newName)
{
// limit name to 100 characters
title = newName.Substring(0, Mathf.Min(newName.Length, 100));
_customData.title = title;
SaveNewData();
}
protected override void OnElementsAdded(IEnumerable<GraphElement> elements)
{
//Enumerate first to prevent multiple-enumeration
IEnumerable<GraphElement> graphElements = elements as GraphElement[] ?? elements.ToArray();
base.OnElementsAdded(graphElements);
foreach (var element in graphElements)
{
if (!_customData.containedElements.Contains(element.GetUid()))
{
_customData.containedElements.Add(element.GetUid());
}
switch (element)
{
// Set group variable on UdonNodes
case UdonNode node:
node.group = this;
node.BringToFront();
break;
case UdonComment comment:
comment.group = this;
break;
}
}
SaveNewData();
}
protected override void OnElementsRemoved(IEnumerable<GraphElement> elements)
{
//Enumerate first to prevent multiple-enumeration
IEnumerable<GraphElement> graphElements = elements as GraphElement[] ?? elements.ToArray();
base.OnElementsRemoved(graphElements);
foreach (var element in graphElements)
{
if (!_customData.containedElements.Contains(element.GetUid())) continue;
_customData.containedElements.Remove(element.GetUid());
switch (element)
{
case UdonNode node:
node.group = null;
break;
case UdonComment comment:
comment.group = null;
break;
}
}
SaveNewData();
}
public UdonGraphElementData GetData()
{
return new UdonGraphElementData(UdonGraphElementType.UdonGroup, Uid, EditorJsonUtility.ToJson(_customData));
}
// CustomData is serialized in user assets, so we can't rename/modify/remove any of these variables
// ReSharper disable InconsistentNaming
// ReSharper disable NotAccessedField.Local
// ReSharper disable FieldCanBeMadeReadOnly.Local
private class CustomData
{
public string uid;
public Rect layout;
public List<string> containedElements = new List<string>();
public string title;
public int layer;
public Color elementTypeColor;
}
// ReSharper enable InconsistentNaming
// ReSharper enable NotAccessedField.Local
// ReSharper enable FieldCanBeMadeReadOnly.Local
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b8045222a10ce04b815642b9cd5ca17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcd657bc1dcf357448d27bcfa8c5dc36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2bca285c1c0c4a5cb2f03599b086abe4
timeCreated: 1623890899

View File

@ -0,0 +1,24 @@
using EngineUI = UnityEngine.UIElements;
using EditorUI = UnityEngine.UIElements;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView.UdonNodes
{
public class GetOrSetProgramVariableNode : UdonNode
{
private EditorUI.PopupField<string> _programVariablePopup;
public GetOrSetProgramVariableNode(UdonNodeDefinition nodeDefinition, UdonGraph view, UdonNodeData nodeData = null) :
base(nodeDefinition, view, nodeData)
{
}
protected override void Initialize()
{
base.Initialize();
_programVariablePopup =
this.GetProgramPopup(UdonNodeExtensions.ProgramPopupType.Variables, _programVariablePopup, true);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cbfa6b1c2cf44feca09853837fc740bb
timeCreated: 1623890922

View File

@ -0,0 +1,22 @@
using EditorGV = UnityEditor.Experimental.GraphView;
using EngineUI = UnityEngine.UIElements;
using EditorUI = UnityEngine.UIElements;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView.UdonNodes
{
public class SendCustomEventNode : UdonNode
{
private EditorUI.PopupField<string> _eventNamePopup;
public SendCustomEventNode(UdonNodeDefinition nodeDefinition, UdonGraph view, UdonNodeData nodeData = null) : base(nodeDefinition, view, nodeData)
{
}
protected override void Initialize()
{
base.Initialize();
_eventNamePopup = this.GetProgramPopup(UdonNodeExtensions.ProgramPopupType.Events, _eventNamePopup, allowEventsWithParameters: definition.fullName.Contains("__SendCustomNetworkEvent__"));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8164fc2c5c5b43428503cf064e8b53f0
timeCreated: 1624411228

View File

@ -0,0 +1,30 @@
using EditorGV = UnityEditor.Experimental.GraphView;
using EngineUI = UnityEngine.UIElements;
using EditorUI = UnityEditor.UIElements;
using System.Linq;
using UnityEngine;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView.UdonNodes
{
public class SetReturnValueNode : UdonNode
{
public SetReturnValueNode(UdonNodeDefinition nodeDefinition, UdonGraph view, UdonNodeData nodeData = null) : base(nodeDefinition, view, nodeData)
{
}
protected override void Initialize()
{
base.Initialize();
const string returnVariable = UdonBehaviour.ReturnVariableName;
string uuid = !Graph.VariableNames.Contains(returnVariable) ? Graph.AddNewVariable("Variable_SystemObject", returnVariable) :
Graph.VariableNodes.FirstOrDefault(n => (string)n.nodeValues[1].Deserialize() == returnVariable)?.uid;
if (!string.IsNullOrWhiteSpace(uuid))
SetNewValue(uuid, 0);
else
Debug.LogError("Could not find return value name!");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e65118bfe2d43f1ad2412dba47c21ee
timeCreated: 1623892831

View File

@ -0,0 +1,24 @@
using UnityEngine.UIElements;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView.UdonNodes
{
public class SetVariableNode : UdonNode
{
public SetVariableNode(UdonNodeDefinition nodeDefinition, UdonGraph view, UdonNodeData nodeData = null) : base(nodeDefinition, view, nodeData)
{
}
protected override void Initialize()
{
base.Initialize();
UdonPort sendChangePort = portsIn[2];
var toggle = sendChangePort.Q<Toggle>();
if (toggle != null)
{
sendChangePort.Insert(0, toggle);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fd9209fd8a363ee42a49a2afaeb35805
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,187 @@
using System;
using UnityEditor.Experimental.GraphView;
using EditorGV = UnityEditor.Experimental.GraphView;
using EngineUI = UnityEngine.UIElements;
using EditorUI = UnityEngine.UIElements;
using UnityEngine.UIElements;
using System.Collections.Generic;
using System.Linq;
using VRC.Udon.Common;
using VRC.Udon.Compiler.Compilers;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView.UdonNodes
{
public static class UdonNodeExtensions
{
#region Methods for creating popup selectors from Program variables / event
private static readonly HashSet<string> InternalEventNames = new HashSet<string>()
{
"_start", "_update", "_lateUpdate", "_fixedUpdate", "onAnimatorIk", "_onAnimatorMove", "_onAvatarChanged", "_onAvatarEyeHeightChanged", "_onBecameInvisible", "_onBecameVisible",
"_onPlayerCollisionEnter", "_onCollisionEnter", "_onCollisionEnter2D", "_onPlayerCollisionExit", "_onCollisionExit", "_onCollisionExit2D", "_onPlayerCollisionStay", "_onCollisionStay", "_onCollisionStay2D",
"_onPlayerTriggerEnter", "_onTriggerEnter", "_onTriggerEnter2D", "_onPlayerTriggerExit", "_onTriggerExit", "_onTriggerExit2D", "_onPlayerTriggerStay", "_onTriggerStay", "_onTriggerStay2D",
"_onDestroy", "_onDisable", "_onDrawGizmos", "_onDrawGizmosSelected", "_onEnable", "_onJointBreak", "_onJointBreak2D", "_onMouseDown", "_onMouseDrag", "_onMouseEnter", "_onMouseExit", "_onMouseOver", "_onMouseUp", "_onMouseUpAsButton",
"_onPlayerParticleCollision", "_onParticleCollision", "_onParticleTrigger", "_onPostRender", "_onPreCull", "_onPreRender", "_onRenderImage", "_onRenderObject", "_onTransformChildrenChanged", "_onTransformParentChanged", "_onValidate", "_onWillRenderObject",
"_interact", "_onDrop", "_onPickup", "_onPickupUseDown", "_onPickupUseUp", "_onPreSerialization", "_onPostSerialization", "_onDeserialization", "_onVideoEnd", "_onVideoPause", "_onVideoPlay", "_onVideoStart", "_midiNoteOn", "_midiNoteOff", "_midiControlChange",
"_onOwnershipRequest", "_onNetworkReady", "_onOwnershipTransferred", "_onPlayerJoined", "_onPlayerLeft", "_onSpawn", "_onStationEntered", "_onStationExited", "_onPlayerRespawn", "_onPlayerDataUpdated", "_onPlayerRestored", "_onInstanceRestored",
"_onMasterTransferred", "_onDroneTriggerEnter", "_onDroneTriggerStay", "_onDroneTriggerExit", "_onVRCCameraSettingsChanged", "_onVRCQualitySettingsChanged",
#if VRC_ENABLE_PLAYER_PERSISTENCE || VRC_ENABLE_INSTANCE_PERSISTENCE
"_onPersistenceUsageUpdated",
#endif
#if VRC_ENABLE_PLAYER_PERSISTENCE
"_onPlayerDataStorageExceeded", "_onPlayerDataStorageWarning", "_onPlayerObjectStorageExceeded", "_onPlayerObjectStorageWarning",
#endif
};
public enum ProgramPopupType
{
Variables, Events
}
private static List<string> GetCustomEventsFromAsset(AbstractSerializedUdonProgramAsset asset, bool allowEventsWithParameters)
{
// don't return internal event names or VariableChange events
var events = asset.RetrieveProgram().EntryPoints.GetExportedSymbols().Where(e =>
!InternalEventNames.Contains(e) && !e.StartsWithCached(VariableChangedEvent.EVENT_PREFIX)).ToList();
if (!allowEventsWithParameters)
{
for (int i = events.Count - 1; i >= 0 ; i--)
{
var eventName = events[i];
var metadata = asset.GetNetworkCallingMetadata(eventName);
if (metadata != null && metadata.Parameters.Length > 0)
{
// event has parameters, remove
events.RemoveAt(i);
}
}
}
return events;
}
public static EditorUI.PopupField<string> GetProgramPopup(this UdonNode node, ProgramPopupType popupType, EditorUI.PopupField<string> eventNamePopup, bool allowEventsWithParameters)
{
const string placeholder = "----";
const string missing = "MISSING! Was";
List<string> options = new List<string>{placeholder};
var data = node.data;
bool unavailable = true;
if(data.nodeUIDs.Length < 1 || string.IsNullOrEmpty(data.nodeUIDs[0]))
{
switch (popupType)
{
case ProgramPopupType.Events:
options = GetCustomEventsFromAsset(node.Graph.graphProgramAsset.SerializedProgramAsset, allowEventsWithParameters);
break;
case ProgramPopupType.Variables:
node.Graph.RefreshVariables(false);
options = new List<string>(node.Graph.VariableNames).Where(x=>!x.StartsWithCached(VariableChangedEvent.OLD_VALUE_PREFIX)).ToList();
break;
default:
throw new ArgumentOutOfRangeException(nameof(popupType), popupType, null);
}
unavailable = options.Count == 0;
options.Insert(0, placeholder);
}
else if(data.InputNodeAtIndex(0)?.fullName == "Get_Variable")
{
// So much work to get the underlying node referenced by a variable. Would be nice to have a method for this.
var parts = data.nodeUIDs[0].Split('|');
if (parts.Length < 1) return null;
string targetId = parts[0];
var variableGetterNode = node.Graph.graphData.FindNode(targetId);
if (variableGetterNode == null || variableGetterNode.nodeValues.Length < 1) return null;
string variableId = variableGetterNode.nodeValues[0].Deserialize() as string;
if (string.IsNullOrWhiteSpace(variableId)) return null;
string variableName = node.Graph.GetVariableName(variableId);
if (string.IsNullOrWhiteSpace(variableName)) return null;
if (node.Graph.udonBehaviour != null && node.Graph.udonBehaviour.publicVariables.TryGetVariableValue(variableName, out UdonBehaviour ub))
{
if (ub != null)
{
switch (popupType)
{
case ProgramPopupType.Events:
options = GetCustomEventsFromAsset(ub.programSource.SerializedProgramAsset, allowEventsWithParameters);
break;
case ProgramPopupType.Variables:
{
if(ub.programSource == null){break;}
if(ub.programSource.SerializedProgramAsset == null){break;}
options = ub.programSource.SerializedProgramAsset.RetrieveProgram()?.SymbolTable
.GetSymbols().Where(s =>
!s.StartsWithCached(UdonGraphCompiler.INTERNAL_VARIABLE_PREFIX) &&
!s.StartsWithCached(VariableChangedEvent.OLD_VALUE_PREFIX)).ToList();
break;
}
}
options?.Insert(0, placeholder);
unavailable = false;
}
}
}
int currentIndex = 0;
int targetNodeValueIndex = node.data.fullName.Contains("SendCustomNetworkEvent") ? 2 : 1;
string targetVarName = data.nodeValues[targetNodeValueIndex].Deserialize() as string;
if (targetVarName != null && targetVarName.StartsWithCached(missing)) targetVarName = null;
// If we have a target variable name:
if (!string.IsNullOrWhiteSpace(targetVarName) && options != null)
{
if (options.Contains(targetVarName))
{
currentIndex = options.IndexOf(targetVarName);
}
else
{
options[0] = unavailable ? targetVarName : $"{missing} {targetVarName}";
}
}
if (eventNamePopup == null)
{
eventNamePopup = new EditorUI.PopupField<string>(options, currentIndex)
{
name = popupType == ProgramPopupType.Events ? "EventNamePopup" : "VariablePopup"
};
var eventNamePort = node.inputContainer.Q(null, popupType == ProgramPopupType.Events ? "eventName" : "symbolName");
eventNamePort?.Add(eventNamePopup);
if (unavailable)
{
eventNamePopup.SetEnabled(false);
}
}
else
{
// Remaking it - remove the old one, at the new one at its previous location
int index = node.inputContainer.IndexOf(eventNamePopup);
eventNamePopup.RemoveFromHierarchy();
eventNamePopup = new EditorUI.PopupField<string>(options, currentIndex);
node.inputContainer.Insert(index, eventNamePopup);
}
eventNamePopup.RegisterValueChangedCallback(
e =>
{
node.SetNewValue(string.Compare(e.newValue, placeholder, StringComparison.Ordinal) == 0 ? "" : e.newValue.ToString(), targetNodeValueIndex);
// Todo: update text field directly and save instead of calling Reload
node.Reload();
});
return eventNamePopup;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00b770580aae40ffb9f6a1d898b52269
timeCreated: 1625678192

View File

@ -0,0 +1,512 @@
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
using EditorUI = UnityEditor.UIElements;
using EngineUI = UnityEngine.UIElements;
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph;
using VRC.Udon.Serialization;
using VRC.SDK3.Network;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
[Serializable]
public class UdonPort : Port
{
private UdonNodeData _udonNodeData;
private int _nodeValueIndex;
private VisualElement _inputField;
private VisualElement _inputFieldTypeLabel;
private IArrayProvider _inspector;
protected UdonPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) :
base(portOrientation, portDirection, portCapacity, type)
{
}
public static Port Create(string portName, Direction portDirection, IEdgeConnectorListener connectorListener,
Type type, UdonNodeData data, int index, Orientation orientation = Orientation.Horizontal)
{
Capacity capacity = Capacity.Single;
if (portDirection == Direction.Input && type == null || portDirection == Direction.Output && type != null)
{
capacity = Capacity.Multi;
}
UdonPort port = new UdonPort(orientation, portDirection, capacity, type)
{
m_EdgeConnector = new EdgeConnector<Edge>(connectorListener),
portName = portName,
_udonNodeData = data,
_nodeValueIndex = index
};
port.SetupPort();
return port;
}
public int GetIndex()
{
return _nodeValueIndex;
}
private bool _isSendChangePort;
private void SetupPort()
{
_isSendChangePort = portName == "sendChange";
if (_isSendChangePort)
{
m_EdgeConnector = null;
}
else
{
this.AddManipulator(m_EdgeConnector);
}
tooltip = UdonGraphExtensions.FriendlyTypeName(portType);
// apply friendly name for custom event output parameters
if (direction == Direction.Output && _udonNodeData.fullName.StartsWithCached("Event_Custom"))
{
if (portType != null)
{
string typeLabel = UdonGraphExtensions.FriendlyTypeName(portType).FriendlyNameify();
SetLabelText($"Param {_nodeValueIndex + 1} ({typeLabel})");
}
}
if (portType == null || direction == Direction.Output)
{
return;
}
if (_udonNodeData.fullName.StartsWithCached("Event_Custom"))
{
HandleFieldForCustomEvent();
}
else if (TryGetValueObject(out object result, portType))
{
MakeField(result);
}
if (_udonNodeData.fullName.StartsWithCached("Const"))
{
RemoveConnectorAndLabel();
}
else if (_udonNodeData.fullName.StartsWithCached("Set_Variable") && _nodeValueIndex == 2)
{
_isSendChangePort = true;
RemoveConnector();
AddToClassList("send-change");
}
AddToClassList(portName);
UpdateLabel(connected);
}
private VisualElement MakeField(object value)
{
var field = UdonFieldFactory.CreateField(
portType,
value,
SetNewValue
);
if (field != null)
{
SetupField(field);
}
return field;
}
private void HandleFieldForCustomEvent()
{
// custom events get some extra fields
if (portName == "EventName")
{
TryGetValueObject(out object result, portType);
var field = MakeField(result);
SetupFieldForCustomEventName(field);
}
else if (portName == "MaxEventsPerSecond")
{
var hasParameters = _udonNodeData.fullName.StartsWithCached("Event_Custom_"); // note the trailing underscore!
if (!TryGetValueObject(out object result, portType) || result is not int storedMax)
{
storedMax = hasParameters ? 5 : 0; // allow 0 for non-parameter events to migrate legacy data
}
if (hasParameters && storedMax == 0)
{
storedMax = 5; // default to 5 for parameter events
}
// write back immediately to ensure default is set
SetNewValue(storedMax);
var field = UdonFieldFactory.CreateField(
typeof(int),
storedMax,
(newValue) =>
{
if (newValue is not int newValueInt)
{
newValueInt = hasParameters ? 5 : 0;
}
if (newValueInt < (hasParameters ? 1 : 0))
{
Debug.LogWarning($"Found invalid MaxEventsPerSecond value of {newValueInt}, applying default.");
newValueInt = hasParameters ? 5 : 0;
}
else if (newValueInt > 100)
{
Debug.LogWarning($"Found large MaxEventsPerSecond value of {newValueInt}, clamping to 100.");
newValueInt = 100;
}
SetNewValue(newValueInt);
}
);
SetupField(field);
RemoveConnector();
}
else if (portName.EndsWith("_type"))
{
TryGetValueObject(out object result, portType);
result ??= typeof(string);
var udonType = VRCUdonSyncTypeConverter.TypeToUdonType(result as Type);
if (udonType == VRCUdonSyncType.NONE)
udonType = VRCUdonSyncType.UdonString;
// write back immediately to ensure default is set
SetNewValue(VRCUdonSyncTypeConverter.UdonTypeToType(udonType));
var field = UdonFieldFactory.CreateField(
typeof(VRCUdonSyncType),
udonType,
(newValue) =>
{
// Convert back to type
var udonType = (VRCUdonSyncType)newValue;
if (udonType == VRCUdonSyncType.NONE)
{
Debug.LogWarning($"Event parameter cannot have type None, defaulting to String.");
udonType = VRCUdonSyncType.UdonString;
}
var type = VRCUdonSyncTypeConverter.UdonTypeToType(udonType);
SetNewValue(type);
// Disconnect anything from the corresponding output port, since the type changed
var eventNode = (UdonNode)node;
eventNode.DisconnectUdonPort(eventNode.portsOut[_nodeValueIndex - 2]);
// Update the port to show the new type
this.Reload();
}
);
SetupField(field);
RemoveConnector();
SetLabelText($"Param {_nodeValueIndex - 1} Type");
}
}
private void SetupField(VisualElement field)
{
// Add label, shown when input is connected. Not shown by default
var friendlyName = UdonGraphExtensions.FriendlyTypeName(portType).FriendlyNameify();
var label = new Label(friendlyName);
_inputFieldTypeLabel = label;
field.AddToClassList("portField");
_inputField = field;
Add(_inputField);
}
private void SetupFieldForCustomEventName(VisualElement field)
{
// Custom Event fields need their event names sanitized after input and their connectors removed
var tField = (TextField) field;
tField.RegisterValueChangedCallback(
(e) =>
{
string newValue = e.newValue.SanitizeVariableName();
tField.value = newValue;
SetNewValue(newValue);
});
RemoveConnectorAndLabel();
field.AddToClassList("portFieldCustomEvent"); // slightly different spacing
}
private void RemoveConnectorAndLabel()
{
RemoveConnector();
this.Q(null, "connectorText")?.RemoveFromHierarchy();
}
private void RemoveConnector()
{
this.Q("connector")?.RemoveFromHierarchy();
}
private void SetLabelText(string text)
{
if (this.Q(null, "connectorText") is Label label)
{
label.text = text;
}
}
#pragma warning disable 0649 // variable never assigned
// ReSharper disable once UnassignedField.Local
private Button _editArrayButton;
#pragma warning restore 0649
private void EditArray(Type elementType)
{
// Update Values when 'Save' is clicked
if (_inspector != null)
{
// Update Values
SetNewValue(_inspector.GetValues());
// Remove Inspector
_inspector.RemoveFromHierarchy();
_inspector = null;
// Update Button Text
_editArrayButton.text = "Edit";
return;
}
// Otherwise set up the inspector
_editArrayButton.text = "Save";
// Get value object, null is ok
TryGetValueObject(out object value);
// Create it new
Type typedArrayInspector = (typeof(UdonArrayInspector<>)).MakeGenericType(elementType);
_inspector = (Activator.CreateInstance(typedArrayInspector, value) as IArrayProvider);
parent.Add(_inspector as VisualElement);
}
// Update elements on connect
public override void Connect(Edge edge)
{
AddToClassList("connected");
base.Connect(edge);
Undo.RecordObject(((UdonNode)node).Graph.graphProgramAsset, "Connect Edge");
// The below logic is just for Output ports
if (edge.input.Equals(this)) return;
// hide field, show label
var input = ((UdonPort) edge.input);
input.UpdateLabel(true);
if (IsReloading())
{
return;
}
// update data
if (portType == null)
{
// We are a flow port
SetFlowUid(((UdonNode) input.node).uid);
this.Compile();
}
else
{
// We are a value port, we need to send our info over to the OTHER node
string myNodeUid = ((UdonNode) node).uid;
input.SetDataFromNewConnection($"{myNodeUid}|{_nodeValueIndex}", input.GetIndex());
}
if (_isSendChangePort)
{
DisconnectAll();
this.Reload();
}
}
public override void OnStopEdgeDragging()
{
base.OnStopEdgeDragging();
if (edgeConnector?.edgeDragHelper?.draggedPort == this)
{
if (capacity == Capacity.Single && connections.Any())
{
// This port could only have one connection. Fixed in Reserialize, need to reload to show the change
this.Reload();
}
}
else
{
this.Reload();
}
}
private void SetFlowUid(string newValue)
{
if (_udonNodeData.flowUIDs.Length <= _nodeValueIndex)
{
// If we don't have space for this flow value, create a new array
// TODO: handle this elsewhere?
var newFlowArray = new string[_nodeValueIndex + 1];
for (int i = 0; i < _udonNodeData.flowUIDs.Length; i++)
{
newFlowArray[i] = _udonNodeData.flowUIDs[i];
}
_udonNodeData.flowUIDs = newFlowArray;
_udonNodeData.flowUIDs.SetValue(newValue, _nodeValueIndex);
}
else
{
_udonNodeData.flowUIDs.SetValue(newValue, _nodeValueIndex);
}
}
public bool IsReloading()
{
return node is UdonNode && ((UdonNode) node).Graph.IsReloading;
}
public void SetDataFromNewConnection(string uidAndPort, int index)
{
// can't do this for Reg stack nodes yet so skipping for demo
if (_udonNodeData == null) return;
if (_udonNodeData.nodeUIDs.Length <= _nodeValueIndex)
{
Debug.Log("Couldn't set it");
}
else
{
_udonNodeData.nodeUIDs.SetValue(uidAndPort, index);
}
}
// Update elements on disconnect
public override void Disconnect(Edge edge)
{
RemoveFromClassList("connected");
if (node == null) return;
Undo.RecordObject(((UdonNode)node).Graph.graphProgramAsset, "Connect Edge");
base.Disconnect(edge);
// hide label, show field
if (direction == Direction.Input)
{
UpdateLabel(false);
}
if (IsReloading())
{
return;
}
// update data
if (direction == Direction.Output && portType == null)
{
// We are a flow port
SetFlowUid("");
this.Compile();
}
else if (direction == Direction.Input && portType != null)
{
// Direction is input
// We are a value port
SetDataFromNewConnection("", GetIndex());
}
}
public void UpdateLabel(bool isConnected)
{
// Port has a 'connected' bool but it doesn't seem to update, so passing 'isConnected' for now
if (isConnected)
{
if (_inputField != null && Contains(_inputField))
{
_inputField.RemoveFromHierarchy();
}
if (_inputFieldTypeLabel != null && !Contains(_inputFieldTypeLabel))
{
Add(_inputFieldTypeLabel);
}
if (_editArrayButton != null && Contains(_editArrayButton))
{
_editArrayButton.RemoveFromHierarchy();
}
}
else
{
if (_inputField != null && !Contains(_inputField))
{
Add(_inputField);
}
if (_inputFieldTypeLabel != null && Contains(_inputFieldTypeLabel))
{
_inputFieldTypeLabel.RemoveFromHierarchy();
}
if (_editArrayButton != null && !Contains(_editArrayButton))
{
Add(_editArrayButton);
}
}
}
private bool TryGetValueObject(out object result, Type type = null)
{
// Initialize out object
result = null;
// get container from node values
SerializableObjectContainer container = _udonNodeData.nodeValues[_nodeValueIndex];
// Null check, failure
if (container == null)
return false;
// Deserialize into result, return failure on null
result = container.Deserialize();
// Strings will deserialize as null, that's ok
if (type == null || type == typeof(string))
{
return true;
}
// any other type is not ok to be null
else if (result == null)
{
return false;
}
// Success - return true
return type.IsInstanceOfType(result);
}
private void SetNewValue(object newValue)
{
_udonNodeData.nodeValues[_nodeValueIndex] = SerializableObjectContainer.Serialize(newValue, portType);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f83d1d3578dd28498c71a980bca86dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,162 @@
using System;
using System.Linq;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonSidebar : GraphElement
{
private readonly UdonGraph _graph;
public readonly UdonGraphVariables GraphVariables;
public readonly UdonGraphEvents EventsList;
private readonly ScrollView _scrollView;
public readonly UdonGraphGroups GroupsList;
private readonly VisualElement _searchBlock;
private readonly TextField _searchField;
private GraphElement[] _searchResults;
private int _focusedResult;
private bool _isBorderHeld;
private float _sidebarWidth = 234f;
public UdonSidebar(UdonGraph graph, UdonSearchManager searchManager, Action<VisualElement, string> editTextRequested)
{
_graph = graph;
name = "UdonSidebar";
_graph.OnMouseMoveCallback += OnGraphMouseMove;
_graph.OnMouseUpCallback += OnGraphMouseUp;
var scrollerPlaceholder = new VisualElement();
scrollerPlaceholder.AddToClassList("scrollerPlaceholder");
Add(scrollerPlaceholder);
VisualElement draggableBorder = new VisualElement();
draggableBorder.AddToClassList("draggableBorder");
Add(draggableBorder);
draggableBorder.RegisterCallback<MouseDownEvent>(evt =>
{
_isBorderHeld = true;
});
_searchBlock = new VisualElement();
_searchBlock.AddToClassList("udonGraphSearch");
Add(_searchBlock);
_searchField = new TextField();
_searchField.AddToClassList("udonGraphSearchInput");
_searchBlock.Add(_searchField);
Label searchPlaceholder = new Label("Search");
searchPlaceholder.AddToClassList("udonGraphSearchPlaceholder");
searchPlaceholder.SetEnabled(false);
_searchField.Add(searchPlaceholder);
_searchField.RegisterValueChangedCallback(e =>
{
string searchText = e.newValue.ToLower();
searchPlaceholder.EnableInClassList("hidden", searchText.Length > 0);
if (searchText.Length > 2)
{
PerformSearch(searchText);
}
});
_searchField.RegisterCallback<KeyDownEvent>(evt =>
{
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
{
NextSearchResult();
_searchField.Focus();
}
});
_scrollView = new ScrollView(ScrollViewMode.Vertical);
Add(_scrollView);
GraphVariables = new UdonGraphVariables(graph, searchManager, editTextRequested);
_scrollView.Add(GraphVariables);
EventsList = new UdonGraphEvents(graph, searchManager);
_scrollView.Add(EventsList);
GroupsList = new UdonGraphGroups(graph);
_scrollView.Add(GroupsList);
// block dragging/moving
RegisterCallback((EventCallback<DragUpdatedEvent>) (e => e.StopPropagation()));
RegisterCallback((EventCallback<WheelEvent>) (e => e.StopPropagation()));
RegisterCallback((EventCallback<MouseDownEvent>) (e => e.StopPropagation()));
RegisterCallback<KeyDownEvent>(_graph.OnKeyDown);
}
public void FocusSearch()
{
_searchField.Q("unity-text-input").Focus();
}
private void PerformSearch(string term)
{
_searchResults = null;
var matching = _graph.graphElements.ToList().Where(i =>
{
if (!(i is UdonNode udonNode))
{
return i.title.ToLower().Contains(term);
}
string nodeName = udonNode.definition.fullName.Split(' ').FirstOrDefault();
if (nodeName != null && nodeName.ToLower()
.Contains(term))
{
return true;
}
return i.title.ToLower().Contains(term);
}).ToArray();
if (matching.Length <= 0) return;
_searchResults = matching;
_graph.ClearSelection();
foreach (var data in matching)
{
_graph.AddToSelection(data);
}
_graph.FrameSelection();
_focusedResult = 0;
}
private void NextSearchResult()
{
if (_searchResults == null) return;
_focusedResult += 1;
if (_focusedResult > _searchResults.Length)
{
_focusedResult = 1;
}
_graph.ClearSelection();
_graph.AddToSelection(_searchResults[_focusedResult - 1]);
_graph.FrameSelection();
}
private void OnGraphMouseMove(object sender, MouseMoveEvent evt)
{
if (!_isBorderHeld) return;
_sidebarWidth = evt.mousePosition.x + 4f;
contentContainer.style.width = new StyleLength(_sidebarWidth);
_scrollView.style.width = new StyleLength(_sidebarWidth - 4f);
_searchBlock.style.width = new StyleLength(_sidebarWidth - 17f);
GraphVariables.style.width = new StyleLength(_sidebarWidth - 17f);
EventsList.style.width = new StyleLength(_sidebarWidth - 17f);
GroupsList.style.width = new StyleLength(_sidebarWidth - 17f);
_graph.OnSidebarResize.Invoke(_graph, evt);
}
private void OnGraphMouseUp(object sender, MouseUpEvent evt)
{
_isBorderHeld = false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 600349daea5c49d38adcfc76129b464c
timeCreated: 1641409648

View File

@ -0,0 +1,6 @@
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonStackNode
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d5984c5be753da439b8a33ffbee8d36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
using System;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonVariableRow : VisualElement
{
private VisualElement m_Root;
private Button m_ExpandButton;
private VisualElement m_ItemContainer;
private VisualElement m_PropertyViewContainer;
private UdonParameterField m_item;
private UdonParameterProperty m_propertyView;
private bool m_Expanded = true;
/// <summary>
/// <para>Indicates whether the BlackboardRow is expanded.</para>
/// </summary>
public bool expanded
{
get => m_Expanded;
set
{
if (m_Expanded == value)
return;
m_Expanded = value;
if (m_Expanded)
{
m_Root.Add(m_PropertyViewContainer);
AddToClassList(nameof (expanded));
}
else
{
m_Root.Remove(m_PropertyViewContainer);
RemoveFromClassList(nameof (expanded));
}
}
}
/// <summary>
/// <para>Constructs a BlackboardRow from a VisualElement and its associated property view. The VisualElement is usually a BlackboardField.</para>
/// </summary>
/// <param name="item">The item that fills the content of this BlackboardRow.</param>
/// <param name="propertyView">The property view related to the content of this BlackboardRow.</param>
public UdonVariableRow(UdonParameterField item, UdonParameterProperty propertyView)
{
VisualTreeAsset visualTreeAsset = EditorGUIUtility.Load("UXML/GraphView/BlackboardRow.uxml") as VisualTreeAsset;
var styleSheet = EditorGUIUtility.Load("StyleSheets/GraphView/Blackboard.uss") as StyleSheet;
this.styleSheets.Add(styleSheet);
VisualElement visualElement = visualTreeAsset.CloneTree();
visualElement.AddToClassList("mainContainer");
m_Root = visualElement.Q("root");
m_ItemContainer = visualElement.Q("itemContainer");
m_PropertyViewContainer = visualElement.Q("propertyViewContainer");
m_ExpandButton = visualElement.Q<Button>("expandButton");
m_ExpandButton.clickable.clicked += () => expanded = !expanded;
Add(visualElement);
ClearClassList();
AddToClassList("blackboardRow");
m_ItemContainer.Add(item);
m_PropertyViewContainer.Add(propertyView);
expanded = false;
m_item = item;
m_propertyView = propertyView;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5eb9a93bc4c04034bfc8cbb36063588b
timeCreated: 1663799634

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 134663bd781d7c44da7d4ef26c3345b9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonEventTypeWindow : UdonSearchWindowBase
{
private List<SearchTreeEntry> _fullRegistry;
#region ISearchWindowProvider
public override List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (!skipCache && _fullRegistry != null) return _fullRegistry;
_fullRegistry = new List<SearchTreeEntry> { new SearchTreeGroupEntry(new GUIContent("Events")) };
var definitions = UdonEditorManager.Instance.GetNodeDefinitions("Event_").ToList().OrderBy(n=>n.name);
foreach (var definition in definitions)
{
_fullRegistry.Add(new SearchTreeEntry(new GUIContent($"Event {definition.name}"))
{
level = 1,
userData = definition,
});
}
return _fullRegistry;
}
public override bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
if (_graphView == null || !(entry.userData is UdonNodeDefinition definition) ||
_graphView.IsDuplicateEventNode(definition.fullName)) return false;
_graphView.AddNodeFromSearch(definition, GetGraphPositionFromContext(context));
return true;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9a3f47510be4a35893681f29632b399
timeCreated: 1641407129

View File

@ -0,0 +1,64 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
#else
using UnityEditor.Experimental.UIElements.GraphView;
#endif
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph;
using VRC.Udon.Graph.Interfaces;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonFocusedSearchWindow : UdonSearchWindowBase
{
public INodeRegistry targetRegistry;
internal List<SearchTreeEntry> _fullRegistry;
#region ISearchWindowProvider
public override List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
_fullRegistry = new List<SearchTreeEntry>();
Texture2D icon = EditorGUIUtility.FindTexture("cs Script Icon");
var registryName = GetSimpleNameForRegistry(targetRegistry);
_fullRegistry.Add(new SearchTreeGroupEntry(new GUIContent($"{registryName} Search"), 0));
// add Registry Level
AddEntriesForRegistry(_fullRegistry, targetRegistry, 1, true);
return _fullRegistry;
}
public override bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
if (entry.userData is UdonNodeDefinition definition && !_graphView.IsDuplicateEventNode(definition.fullName))
{
_graphView.AddNodeFromSearch(definition, GetGraphPositionFromContext(context));
return true;
}
else
{
return false;
}
}
// TODO: move this to Extension
private string GetSimpleNameForRegistry(INodeRegistry registry)
{
string registryName = registry.ToString().Replace("NodeRegistry", "").FriendlyNameify();
registryName = registryName.Substring(registryName.LastIndexOf(".") + 1);
registryName = registryName.Replace("UnityEngine", "");
return registryName;
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6581176c97993bb40976acff208bd0b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using UnityEditor.Experimental.GraphView;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using VRC.Udon.Graph;
using VRC.Udon.Graph.Interfaces;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonFullSearchWindow : UdonSearchWindowBase
{
private static List<SearchTreeEntry> _slowRegistryCache;
#region ISearchWindowProvider
public override List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (!skipCache && _slowRegistryCache != null && _slowRegistryCache.Count > 0) return _slowRegistryCache;
_slowRegistryCache = new List<SearchTreeEntry>
{
new SearchTreeGroupEntry(new GUIContent("Full Search"))
};
var topRegistries = UdonEditorManager.Instance.GetTopRegistries();
foreach (var topRegistry in topRegistries)
{
string topName = topRegistry.Key.Replace("NodeRegistry", "");
if (topName != "Udon")
{
_slowRegistryCache.Add(new SearchTreeGroupEntry(new GUIContent(topName), 1));
}
// get all registries, save into registryName > INodeRegistry Lookup
var subRegistries = new Dictionary<string, INodeRegistry>();
foreach (KeyValuePair<string, INodeRegistry> registry in topRegistry.Value.OrderBy(s => s.Key))
{
string baseRegistryName = registry.Key.Replace("NodeRegistry", "").FriendlyNameify().ReplaceFirst(topName, "");
string registryName = baseRegistryName.UppercaseFirst();
subRegistries.Add(registryName, registry.Value);
}
// Go through each registry entry and add the top-level registry and associated array registry
foreach (KeyValuePair<string, INodeRegistry> regEntry in subRegistries)
{
INodeRegistry registry = regEntry.Value;
string registryName = regEntry.Key;
int level = 2;
// Special cases for Udon sub-levels, added at top
if (topName == "Udon")
{
level = 1;
if (registryName == "Event" || registryName == "Type")
{
registryName = $"{registryName}s";
}
}
if (!registryName.EndsWith("[]"))
{
// add Registry Level
var groupEntry = new SearchTreeGroupEntry(new GUIContent(registryName, (Texture2D)null), level) { userData = registry };
_slowRegistryCache.Add(groupEntry);
}
// Check for Array Type first
string regArrayType = $"{registryName}[]";
if (subRegistries.TryGetValue(regArrayType, out INodeRegistry arrayRegistry))
{
// we have a matching subRegistry, add that next
var arrayLevel = level + 1;
var arrayGroupEntry = new SearchTreeGroupEntry(new GUIContent(regArrayType, (Texture2D)null), arrayLevel) { userData = registry };
_slowRegistryCache.Add(arrayGroupEntry);
// Add all array entries
AddEntriesForRegistry(_slowRegistryCache, arrayRegistry, arrayLevel + 1);
}
AddEntriesForRegistry(_slowRegistryCache, registry, level + 1, true);
}
}
return _slowRegistryCache;
}
public override bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
// checking type so we can support selecting registries as well
if (entry.userData is UdonNodeDefinition definition && !_graphView.IsDuplicateEventNode(definition.fullName))
{
_graphView.AddNodeFromSearch(definition, GetGraphPositionFromContext(context));
return true;
}
else
{
return false;
}
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b721120e6c1d320448a55fe87a7de824
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,124 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
#else
using UnityEditor.Experimental.UIElements.GraphView;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonPortSearchWindow : UdonSearchWindowBase
{
internal List<SearchTreeEntry> _fullRegistry;
#region ISearchWindowProvider
public Type typeToSearch;
public UdonPort startingPort;
public Direction direction;
public class VariableInfo
{
public string uid;
public bool isGetter;
public VariableInfo(string uid, bool isGetter)
{
this.uid = uid;
this.isGetter = isGetter;
}
}
override public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
_fullRegistry = new List<SearchTreeEntry>
{
new SearchTreeGroupEntry(new GUIContent($"{direction.ToString()} Search"), 0)
};
var defsToAdd = new Dictionary<string, List<UdonNodeDefinition>>();
var registries = UdonEditorManager.Instance.GetNodeRegistries();
foreach (var item in registries)
{
var definitions = item.Value.GetNodeDefinitions().ToList();
var registryName = item.Key.FriendlyNameify().Replace("NodeRegistry", "");
defsToAdd.Add(registryName, new List<UdonNodeDefinition>());
foreach (var def in definitions)
{
var collection = direction == Direction.Input ? def.Inputs : def.Outputs;
if(collection.Any(p=>p.type == typeToSearch))
{
defsToAdd[registryName].Add(def);
}
}
}
var variables = _graphView.VariableNodes;
// Add Getters and Setters for matched variable types
Texture2D icon = EditorGUIUtility.FindTexture("GameManager Icon");
string typeToSearchSimple = typeToSearch.ToString().Replace(".", "");
foreach (var item in variables)
{
string variableSimpleName = item.fullName.Replace("Variable_", "");
string getOrSet = direction == Direction.Output ? "Get" : "Set";
if(variableSimpleName == typeToSearchSimple)
{
string customVariableName = item.nodeValues[1].Deserialize().ToString();
_fullRegistry.Add(new SearchTreeEntry(new GUIContent($"{getOrSet} {customVariableName}", icon))
{
level = 1,
userData = new VariableInfo(item.uid, direction == Direction.Output),
});
}
}
foreach (var item in defsToAdd)
{
// Skip empty lists
if (item.Value.Count == 0) continue;
_fullRegistry.Add(new SearchTreeGroupEntry(new GUIContent(item.Key), 1));
AddEntries(_fullRegistry, item.Value, 2);
}
return _fullRegistry;
}
public override bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
var position = GetGraphPositionFromContext(context) - new Vector2(140, 0);
// checking type so we can support selecting registries as well
if (entry.userData is UdonNodeDefinition definition && !_graphView.IsDuplicateEventNode(definition.fullName))
{
var node = _graphView.AddNodeFromSearch(definition, position);
_graphView.ConnectNodeTo(node, startingPort, direction, typeToSearch);
return true;
}
else if(entry.userData is VariableInfo data)
{
UdonNode node = _graphView.MakeVariableNode(data.uid, position, data.isGetter ? GraphView.UdonGraph.VariableNodeType.Getter : GraphView.UdonGraph.VariableNodeType.Setter );
_graphView.AddElement(node);
_graphView.ConnectNodeTo(node, startingPort, direction, typeToSearch);
_graphView.RefreshVariables(true);
return true;
}
else
{
return false;
}
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e94c084f399869b42a21244fd07778c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
#else
using UnityEditor.Experimental.UIElements.GraphView;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph;
using VRC.Udon.Graph.Interfaces;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonRegistrySearchWindow : UdonSearchWindowBase
{
private UdonSearchManager _searchManager;
private static List<SearchTreeEntry> _registryCache;
private List<(string, string)> _shortcutRegistries = new List<(string, string)>()
{
("UnityEngine","Debug"),
("Udon","Special"),
("Udon","Type"),
("VRC", "UdonCommonInterfacesIUdonEventReceiver")
};
private HashSet<string> _hiddenRegistries = new HashSet<string>()
{
};
public static readonly HashSet<string> TopNamesToKeep = new HashSet<string>()
{
"UnityEngineRandom", "SystemRandom", "SystemRandom[]", "SystemRandomArray"
};
public void Initialize(UdonGraphWindow editorWindow, UdonGraph graphView, UdonSearchManager manager)
{
base.Initialize(editorWindow, graphView);
_searchManager = manager;
}
#region ISearchWindowProvider
override public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (!skipCache && (_registryCache != null && _registryCache.Count > 0)) return _registryCache;
_registryCache = new List<SearchTreeEntry>();
Texture2D icon = EditorGUIUtility.FindTexture("cs Script Icon");
_registryCache.Add(new SearchTreeGroupEntry(new GUIContent("Quick Search"), 0));
var topRegistriesLookup = new Dictionary<string, List<KeyValuePair<string, INodeRegistry>>>();
foreach (var entry in UdonEditorManager.Instance.GetTopRegistries())
{
topRegistriesLookup.Add(entry.Key, new List<KeyValuePair<string, INodeRegistry>>(entry.Value));
}
// Add shortcut registries
foreach (var item in _shortcutRegistries)
{
if (topRegistriesLookup.TryGetValue(item.Item1, out var searchRegistry))
{
string subRegistryName = $"{item.Item1}{item.Item2}NodeRegistry";
var subRegistry = searchRegistry.FindAll(r => r.Key == subRegistryName);
if (subRegistry.Count == 1)
{
topRegistriesLookup.Add(item.Item2, subRegistry);
}
else
{
Debug.LogWarning($"Could not find sub-registry {subRegistryName}");
}
}
}
// Combine Events into special top-level element
var vrcEvents = topRegistriesLookup["VRC"].Find(r=>r.Key == "VRCEventNodeRegistry").Value.GetNodeDefinitions();
var udonEvents = topRegistriesLookup["Udon"].Find(r => r.Key == "UdonEventNodeRegistry").Value.GetNodeDefinitions();
var allEvents = vrcEvents.Concat(udonEvents);
var eventRegistry = new EventRegistry();
foreach (var item in allEvents)
{
eventRegistry.definitions.Add(item);
}
topRegistriesLookup.Add("Events", new List<KeyValuePair<string, INodeRegistry>>() { new KeyValuePair<string, INodeRegistry>("Events", eventRegistry) });
// Build lookup from newly organized list
var topRegistries = topRegistriesLookup.OrderBy(s => s.Key);
foreach (var topRegistry in topRegistries)
{
string topName = topRegistry.Key.Replace("NodeRegistry", "");
// Handle Shortcut registries with only 1 top-level registry listed
if (topRegistry.Value.Count == 1)
{
string registryName = UdonGraphExtensions.FriendlyNameify(topName);
_registryCache.Add(new SearchTreeGroupEntry(new GUIContent(registryName)) { level = 1, userData = topRegistry.Value });
AddEntriesForRegistry(_registryCache, topRegistry.Value.First().Value, 2);
continue;
}
// Skip empty 'Udon' top level
if (topName != "Udon")
{
_registryCache.Add(new SearchTreeGroupEntry(new GUIContent(topName), 1));
}
foreach (KeyValuePair<string, INodeRegistry> registry in topRegistry.Value.OrderBy(s => s.Key))
{
string baseRegistryName = registry.Key.Replace("NodeRegistry", "").FriendlyNameify();
// Remove topname registries from name, except for some special cases like UnityEngineRandom/SystemRandom
if (!TopNamesToKeep.Contains(baseRegistryName))
{
baseRegistryName = baseRegistryName.ReplaceFirst(topName, "");
}
string registryName = baseRegistryName.UppercaseFirst();
// Plural-ize Event->Events and Type->Types
if (topName == "Udon" && (registryName == "Event" || registryName == "Type"))
{
registryName = $"{registryName}s";
}
else
{
// add Registry Level
if (registryName.StartsWithCached("Object") || registryName.StartsWithCached("Type"))
{
registryName = $"{topName}.{registryName}";
}
// skip certain registries
if (_hiddenRegistries.Contains(registryName))
{
continue;
}
_registryCache.Add(new SearchTreeEntry(new GUIContent(registryName, icon, $"{topName}.{registryName}")) { level = 2, userData = registry.Value });
}
}
}
return _registryCache;
}
public override bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
// checking type so we can support selecting registries as well
if (entry.userData is INodeRegistry)
{
_searchManager.QueueOpenFocusedSearch(entry.userData as INodeRegistry, context.screenMousePosition);
return true;
}
else if (entry.userData is UdonNodeDefinition definition && !_graphView.IsDuplicateEventNode(definition.fullName))
{
_graphView.AddNodeFromSearch(definition, GetGraphPositionFromContext(context));
return true;
}
else
{
return false;
}
}
#endregion
public class EventRegistry : INodeRegistry
{
public List<UdonNodeDefinition> definitions = new List<UdonNodeDefinition>();
public UdonNodeDefinition GetNodeDefinition(string identifier)
{
throw new NotImplementedException();
}
public IEnumerable<UdonNodeDefinition> GetNodeDefinitions()
{
return definitions;
}
public IEnumerable<UdonNodeDefinition> GetNodeDefinitions(string baseIdentifier)
{
throw new NotImplementedException();
}
public Dictionary<string, INodeRegistry> GetNodeRegistries()
{
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a6c453fae11b5349a33399e258d1578
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,131 @@
using UnityEditor.Experimental.GraphView;
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph.Interfaces;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonSearchManager
{
private readonly UdonGraph _view;
private readonly UdonGraphWindow _window;
// Search Windows
private UdonFocusedSearchWindow _focusedSearchWindow;
private UdonRegistrySearchWindow _registrySearchWindow;
private UdonFullSearchWindow _fullSearchWindow;
private UdonVariableTypeWindow _variableSearchWindow;
private UdonPortSearchWindow _portSearchWindow;
private UdonEventTypeWindow _eventSearchWindow;
public UdonSearchManager(UdonGraph view, UdonGraphWindow window)
{
_view = view;
_window = window;
SetupSearchTypes();
view.nodeCreationRequest += OnRequestNodeCreation;
}
private void SetupSearchTypes()
{
if (_registrySearchWindow == null)
_registrySearchWindow = ScriptableObject.CreateInstance<UdonRegistrySearchWindow>();
_registrySearchWindow.Initialize(_window, _view, this);
if (_fullSearchWindow == null)
_fullSearchWindow = ScriptableObject.CreateInstance<UdonFullSearchWindow>();
_fullSearchWindow.Initialize(_window, _view);
if (_focusedSearchWindow == null)
_focusedSearchWindow = ScriptableObject.CreateInstance<UdonFocusedSearchWindow>();
_focusedSearchWindow.Initialize(_window, _view);
if (_variableSearchWindow == null)
_variableSearchWindow = ScriptableObject.CreateInstance<UdonVariableTypeWindow>();
_variableSearchWindow.Initialize(_window, _view);
if (_portSearchWindow == null)
_portSearchWindow = ScriptableObject.CreateInstance<UdonPortSearchWindow>();
_portSearchWindow.Initialize(_window, _view);
if (_eventSearchWindow == null)
_eventSearchWindow = ScriptableObject.CreateInstance<UdonEventTypeWindow>();
_eventSearchWindow.Initialize(_window, _view);
}
private void OnRequestNodeCreation(NodeCreationContext context)
{
// started on empty space
if (context.target == null)
{
// If we have a node selected (but not set as context.target because that's a container for new nodes to go into), search within that node's registry
if (Settings.SearchOnSelectedNodeRegistry && _view.selection.Count > 0 &&
_view.selection.First() is UdonNode)
{
_focusedSearchWindow.targetRegistry = (_view.selection.First() as UdonNode)?.Registry;
SearchWindow.Open(new SearchWindowContext(context.screenMousePosition, 360, 360),
_focusedSearchWindow);
}
else
{
// Create Search Window that only searches Top-Level Registries
SearchWindow.Open(new SearchWindowContext(context.screenMousePosition, 360, 360),
_registrySearchWindow);
}
}
else if (context.target is UdonGraph)
{
// Slightly hacky method to figure out that we want a full-search window
SearchWindow.Open(new SearchWindowContext(context.screenMousePosition, 360, 360), _fullSearchWindow);
}
}
public void OpenVariableSearch(Vector2 screenMousePosition)
{
// offset search window to appear next to mouse
screenMousePosition.x += 217;
screenMousePosition.y += 0;
SearchWindow.Open(new SearchWindowContext(screenMousePosition, 360, 360), _variableSearchWindow);
}
public void OpenEventSearch(Vector2 screenMousePosition)
{
screenMousePosition.x += 217;
screenMousePosition.y += 0;
SearchWindow.Open(new SearchWindowContext(screenMousePosition, 360, 360), _eventSearchWindow);
}
public void OpenPortSearch(Type type, Vector2 screenMousePosition, UdonPort port, Direction direction)
{
// offset search window to appear next to mouse
screenMousePosition = _portSearchWindow._editorWindow.position.position + screenMousePosition;
screenMousePosition.x += 140;
screenMousePosition.y += 0;
_portSearchWindow.typeToSearch = type;
_portSearchWindow.startingPort = port;
_portSearchWindow.direction = direction;
SearchWindow.Open(new SearchWindowContext(screenMousePosition, 360, 360), _portSearchWindow);
}
private Vector2 _searchWindowPosition;
public void QueueOpenFocusedSearch(INodeRegistry registry, Vector2 position)
{
_searchWindowPosition = position;
_focusedSearchWindow.targetRegistry = registry;
EditorApplication.update += TryOpenFocusedSearch;
}
private void TryOpenFocusedSearch()
{
if (SearchWindow.Open(new SearchWindowContext(_searchWindowPosition, 360, 360), _focusedSearchWindow))
{
EditorApplication.update -= TryOpenFocusedSearch;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5a10bb1987c27944bd08a88119b2844
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,195 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
#else
using UnityEditor.Experimental.UIElements.GraphView;
using UnityEngine.Experimental.UIElements;
using UnityEditor.Experimental.UIElements;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Graph;
using VRC.Udon.Graph.Interfaces;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonSearchWindowBase : ScriptableObject, ISearchWindowProvider
{
// Reference to actual Graph View
internal UdonGraph _graphView;
private List<SearchTreeEntry> _exampleLookup;
internal UdonGraphWindow _editorWindow;
protected bool skipCache = false;
private readonly HashSet<string> nodesToSkip = new HashSet<string>()
{
"Get_Variable",
"Set_Variable",
"Comment",
"Event_OnVariableChange",
};
public virtual void Initialize(UdonGraphWindow editorWindow, UdonGraph graphView)
{
_editorWindow = editorWindow;
_graphView = graphView;
}
#region ISearchWindowProvider
public virtual List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (!skipCache && ( _exampleLookup != null && _exampleLookup.Count > 0)) return _exampleLookup;
_exampleLookup = new List<SearchTreeEntry>();
Texture2D icon = EditorGUIUtility.FindTexture("cs Script Icon");
_exampleLookup.Add(new SearchTreeGroupEntry(new GUIContent("Create Node"), 0));
return _exampleLookup;
}
public virtual bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
return true;
}
#endregion
internal Vector2 GetGraphPositionFromContext(SearchWindowContext context)
{
#if UNITY_2019_3_OR_NEWER
var windowRoot = _editorWindow.rootVisualElement;
#else
var windowRoot = _editorWindow.GetRootVisualContainer();
#endif
var windowMousePosition = windowRoot.ChangeCoordinatesTo(windowRoot.parent,
context.screenMousePosition - _editorWindow.position.position);
var graphMousePosition = _graphView.contentViewContainer.WorldToLocal(windowMousePosition);
return graphMousePosition;
}
internal void AddEntries(List<SearchTreeEntry> cache, IEnumerable<UdonNodeDefinition> definitions, int level,
bool stripToLastDot = false)
{
Texture2D icon = AssetPreview.GetMiniTypeThumbnail(typeof(GameObject));
Texture2D iconGetComponents = EditorGUIUtility.FindTexture("d_ViewToolZoom");
Texture2D iconOther = new Texture2D(1, 1);
iconOther.SetPixel(0,0, new Color(0,0,0,0));
iconOther.Apply();
Dictionary<string, UdonNodeDefinition> baseNodeDefinition = new Dictionary<string, UdonNodeDefinition>();
foreach (UdonNodeDefinition nodeDefinition in definitions.OrderBy(
s => UdonGraphExtensions.PrettyFullName(s)))
{
string baseIdentifier = nodeDefinition.fullName;
string[] splitBaseIdentifier = baseIdentifier.Split(new[] { "__" }, StringSplitOptions.None);
if (splitBaseIdentifier.Length >= 2)
{
baseIdentifier = $"{splitBaseIdentifier[0]}__{splitBaseIdentifier[1]}";
}
if (baseNodeDefinition.ContainsKey(baseIdentifier))
{
continue;
}
baseNodeDefinition.Add(baseIdentifier, nodeDefinition);
}
var nodesOfGetComponentType = new List<SearchTreeEntry>();
var nodesOfOtherType = new List<SearchTreeEntry>();
// add all subTypes
foreach (KeyValuePair<string, UdonNodeDefinition> nodeDefinitionsEntry in baseNodeDefinition)
{
string nodeName = UdonGraphExtensions.PrettyBaseName(nodeDefinitionsEntry.Key);
nodeName = nodeName.UppercaseFirst();
nodeName = nodeName.Replace("_", " ");
if (stripToLastDot)
{
int lastDotIndex = nodeName.LastIndexOf('.');
nodeName = nodeName.Substring(lastDotIndex + 1);
}
// Skip some nodes that should be added in other ways (variables and comments)
if (nodeName.StartsWithCached("Variable") || nodesToSkip.Contains(nodeDefinitionsEntry.Key))
{
continue;
}
if (nodeName.StartsWithCached("Object"))
{
nodeName = $"{nodeDefinitionsEntry.Value.type.Namespace}.{nodeName}";
}
if (nodeNamesGetComponentType.Contains(nodeName))
{
nodesOfGetComponentType.Add(new SearchTreeEntry(new GUIContent(nodeName, iconGetComponents)) { level = level+1, userData = nodeDefinitionsEntry.Value });
continue;
}
// Only put 'Equals' in the 'Other' category if this definition is not an Enum
if (nodeNamesOtherType.Contains(nodeName) || nodeName == "Equals" && !nodeDefinitionsEntry.Value.type.IsEnum)
{
nodesOfOtherType.Add(new SearchTreeEntry(new GUIContent(nodeName, iconOther)) { level = level+1, userData = nodeDefinitionsEntry.Value });
continue;
}
cache.Add(new SearchTreeEntry(new GUIContent(nodeName, icon)) { level = level, userData = nodeDefinitionsEntry.Value });
}
// add getComponents level
if (nodesOfGetComponentType.Count > 0)
{
cache.Add(new SearchTreeGroupEntry(new GUIContent("GetComponents"), level));
foreach (var entry in nodesOfGetComponentType)
{
cache.Add(entry);
}
}
// add other level
if (nodesOfOtherType.Count > 0)
{
cache.Add(new SearchTreeGroupEntry(new GUIContent("Other"), level));
foreach (var entry in nodesOfOtherType)
{
cache.Add(entry);
}
}
}
private static HashSet<string> nodeNamesGetComponentType = new HashSet<string>()
{
"GetComponent",
"GetComponentInChildren",
"GetComponentInParent",
"GetComponents",
"GetComponentsInChildren",
"GetComponentsInParent",
};
private static HashSet<string> nodeNamesOtherType = new HashSet<string>()
{
"Equality",
"GetHashCode",
"GetInstanceID",
"GetType",
"Implicit",
"Inequality",
"Tostring",
};
// adds all entries so we can use this for regular and array registries
internal void AddEntriesForRegistry(List<SearchTreeEntry> cache, INodeRegistry registry, int level,
bool stripToLastDot = false)
{
AddEntries(cache, registry.GetNodeDefinitions(), level, stripToLastDot);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d825ed3ba6aa7f14294e73efefc217d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Experimental.GraphView;
#else
using UnityEditor.Experimental.UIElements.GraphView;
#endif
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
{
public class UdonVariableTypeWindow : UdonSearchWindowBase
{
internal List<SearchTreeEntry> _fullRegistry;
#region ISearchWindowProvider
override public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (!skipCache && _fullRegistry != null) return _fullRegistry;
_fullRegistry = new List<SearchTreeEntry>();
_fullRegistry.Add(new SearchTreeGroupEntry(new GUIContent("Variable Type Search"), 0));
var definitions = UdonEditorManager.Instance.GetNodeDefinitions("Variable_").ToList().OrderBy(n=>n.name);
foreach (var definition in definitions)
{
_fullRegistry.Add(new SearchTreeEntry(new GUIContent(UdonGraphExtensions.FriendlyTypeName(definition.type).FriendlyNameify()))
{
level = 1,
userData = definition.fullName,
});
}
return _fullRegistry;
}
override public bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
if(_graphView == null)
{
return false;
}
_graphView.AddNewVariable((string)entry.userData);
return true;
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More