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: 4043032f88a8ca14ab0daa7f59d4dc56
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using VRC.Udon.Serialization.OdinSerializer;
using VRC.Udon.Serialization.OdinSerializer.Utilities;
using Object = System.Object;
namespace VRC.SDK3.Data.Editor
{
[CustomPropertyDrawer(typeof(DataToken))]
[CustomPropertyDrawer(typeof(DataList))]
[CustomPropertyDrawer(typeof(DataDictionary))]
public class DataPropertyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 30 * EditorGUIUtility.pixelsPerPoint;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.LabelField(position, $"{property.name} no defined editor for type of {property.type}");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fb263072af774326bec32396a5bef7ba
timeCreated: 1675741121

View File

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

View File

@ -0,0 +1,102 @@
fileFormatVersion: 2
guid: 8b6535096cfa29340897276abbdd015f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
fileFormatVersion: 2
guid: 585dd63e377866248b16bdba915820ed
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,46 @@
fileFormatVersion: 2
guid: b335798a4f28bec40ba9b3d4a15acee7
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
fileFormatVersion: 2
guid: 21dcba1a47cc8c84381629950b692129
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 161140ecae894b84ba7bdd6e44ff4371
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 19cff77330d183441a69ff6c69e07629
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using VRC.Udon.Graph.Interfaces;
using VRC.Udon.Graph.Attributes;
using VRC.Udon.Graph.NodeRegistries;
[assembly: UdonGraphNodeRegistry(typeof(VRCInstantiateNodeRegistry), "VRCInstantiateNodeRegistry")]
namespace VRC.Udon.Graph.NodeRegistries
{
public class VRCInstantiateNodeRegistry : BaseNodeRegistry
{
protected override Dictionary<string, INodeRegistry> NextRegistries => _nextRegistries;
private static readonly Dictionary<string, INodeRegistry> _nextRegistries = new Dictionary<string, INodeRegistry>();
protected override Dictionary<string, UdonNodeDefinition> NodeDefinitions => _nodeDefinitions;
private static readonly Dictionary<string, UdonNodeDefinition> _nodeDefinitions = new Dictionary<string, UdonNodeDefinition>
{
{
"VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
new UdonNodeDefinition(
"VRChat Instantiate",
"VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
typeof(UnityEngine.Object),
new []
{
new UdonNodeParameter
{
name = "original",
type = typeof(UnityEngine.GameObject),
parameterType = UdonNodeParameter.ParameterType.IN
},
new UdonNodeParameter
{
name = "clone",
type = typeof(UnityEngine.GameObject),
parameterType = UdonNodeParameter.ParameterType.OUT
}
},
new string[] { },
new string[] { },
new object[] { },
true
)
}
};
}
}

View File

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

View File

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

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;
}
}
}

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