Added Unity project files
This commit is contained in:
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f2c773c057bbd749ad8fd66c018541a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0261d54fb1cc86b46a074b8d6e477cf0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bea6600b38aa0249b02e3d3ee8a7d44
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c66f97d5a5a38cb478fd4df11ece7be7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 602ffcf431b3e4f41a18bd868751439a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e79144dad56140a7bcd0d9f945153784
|
||||
timeCreated: 1632419615
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d9183d7c7ce67448d1e010456e36f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a6b99e05a39452082bd89b0feec45ca
|
||||
timeCreated: 1660169942
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c96407d2b7698c4c8a0476efa2c765d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42747856e1884be6b1112c8838963662
|
||||
timeCreated: 1632419007
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcda1561abdb40c69f9eeb9211bd3e3d
|
||||
timeCreated: 1632419462
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b91b14ff1e24276863f173d0e9c760c
|
||||
timeCreated: 1632419145
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d1c8af690a94ef0a670e5d321733414
|
||||
timeCreated: 1632419341
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1387a4616a8f4c87bd0d55d2ffc021c8
|
||||
timeCreated: 1632419528
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeb751ae1c234a04291c5039626f3470
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22a713ed81ae4c7e9ad58760232ce573
|
||||
timeCreated: 1660170466
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b029e079cc40515438800823a9649f5b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 469db50616185d04e8a46dcd75db12d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f257a6eeae213a4db991d486cace003
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4f0ade55ae13b6468a765826f1f2540
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e5916b8dd19e4445a9156a457b82ee4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba3ecc4c46929404d8c2ec920743b823
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75df7c5698fa41118a907e0f229bb537
|
||||
timeCreated: 1641352883
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2adbf86181214870b117bdc6c99946d8
|
||||
timeCreated: 1641420287
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1889c18ccf7e41c7af45075ff3ad65b6
|
||||
timeCreated: 1641509003
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 346f4d7e451c44daaaacbf470d40c254
|
||||
timeCreated: 1641412295
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b8045222a10ce04b815642b9cd5ca17
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcd657bc1dcf357448d27bcfa8c5dc36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bca285c1c0c4a5cb2f03599b086abe4
|
||||
timeCreated: 1623890899
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbfa6b1c2cf44feca09853837fc740bb
|
||||
timeCreated: 1623890922
|
||||
@ -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__"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8164fc2c5c5b43428503cf064e8b53f0
|
||||
timeCreated: 1624411228
|
||||
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e65118bfe2d43f1ad2412dba47c21ee
|
||||
timeCreated: 1623892831
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd9209fd8a363ee42a49a2afaeb35805
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00b770580aae40ffb9f6a1d898b52269
|
||||
timeCreated: 1625678192
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f83d1d3578dd28498c71a980bca86dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 600349daea5c49d38adcfc76129b464c
|
||||
timeCreated: 1641409648
|
||||
@ -0,0 +1,6 @@
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
|
||||
{
|
||||
public class UdonStackNode
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d5984c5be753da439b8a33ffbee8d36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5eb9a93bc4c04034bfc8cbb36063588b
|
||||
timeCreated: 1663799634
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 134663bd781d7c44da7d4ef26c3345b9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9a3f47510be4a35893681f29632b399
|
||||
timeCreated: 1641407129
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6581176c97993bb40976acff208bd0b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b721120e6c1d320448a55fe87a7de824
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e94c084f399869b42a21244fd07778c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a6c453fae11b5349a33399e258d1578
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5a10bb1987c27944bd08a88119b2844
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d825ed3ba6aa7f14294e73efefc217d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16fc7a7a059deeb458fdcdf719b467a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using UnityEngine;
|
||||
using VRC.Udon.Common.Interfaces;
|
||||
|
||||
// nicked from GraphProcessor project
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram
|
||||
{
|
||||
public static class TypeExtension
|
||||
{
|
||||
public static bool IsReallyAssignableFrom(this Type type, Type otherType)
|
||||
{
|
||||
if (type == null && otherType != null) return false;
|
||||
if (otherType == null && type != null) return false;
|
||||
|
||||
if (type == otherType)
|
||||
return true;
|
||||
if (type.IsAssignableFrom(otherType))
|
||||
return true;
|
||||
if (otherType.IsAssignableFrom(type))
|
||||
return true;
|
||||
if (type == typeof(IUdonEventReceiver) && otherType == typeof(Component))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2f2300f99ce0ea4a8d9a20b464384df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,182 @@
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Components;
|
||||
using VRC.SDK3.Components.Video;
|
||||
using VRC.SDKBase;
|
||||
using VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI
|
||||
{
|
||||
public static class UdonFieldFactory
|
||||
{
|
||||
static readonly Dictionary<Type, Type> fieldDrawers = new Dictionary<Type, Type>();
|
||||
|
||||
static readonly MethodInfo createFieldMethod =
|
||||
typeof(UdonFieldFactory).GetMethod(nameof(CreateFieldSpecific), BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
static readonly HashSet<Type> customEnumTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof(MirrorClearFlags),
|
||||
typeof(VideoError)
|
||||
};
|
||||
|
||||
static UdonFieldFactory()
|
||||
{
|
||||
AddDrawer(typeof(bool), typeof(Toggle));
|
||||
AddDrawer(typeof(int), typeof(IntegerField));
|
||||
AddDrawer(typeof(uint), typeof(UnsignedIntegerField));
|
||||
AddDrawer(typeof(long), typeof(LongField));
|
||||
AddDrawer(typeof(ulong), typeof(UnsignedLongField));
|
||||
AddDrawer(typeof(short), typeof(ShortField));
|
||||
AddDrawer(typeof(ushort), typeof(UnsignedShortField));
|
||||
AddDrawer(typeof(float), typeof(FloatField));
|
||||
AddDrawer(typeof(double), typeof(DoubleField));
|
||||
AddDrawer(typeof(string), typeof(TextField));
|
||||
AddDrawer(typeof(Bounds), typeof(BoundsField));
|
||||
AddDrawer(typeof(Color), typeof(ColorField));
|
||||
AddDrawer(typeof(Vector2), typeof(Vector2Field));
|
||||
AddDrawer(typeof(Vector2Int), typeof(Vector2IntField));
|
||||
AddDrawer(typeof(Vector3), typeof(Vector3Field));
|
||||
AddDrawer(typeof(Vector3Int), typeof(Vector3IntField));
|
||||
AddDrawer(typeof(Vector4), typeof(Vector4Field));
|
||||
AddDrawer(typeof(AnimationCurve), typeof(CurveField));
|
||||
AddDrawer(typeof(Enum), typeof(EnumField));
|
||||
AddDrawer(typeof(Gradient), typeof(GradientField));
|
||||
AddDrawer(typeof(Object), typeof(ObjectField));
|
||||
AddDrawer(typeof(Rect), typeof(RectField));
|
||||
AddDrawer(typeof(RectInt), typeof(RectIntField));
|
||||
AddDrawer(typeof(char), typeof(CharField));
|
||||
AddDrawer(typeof(byte), typeof(ByteField));
|
||||
AddDrawer(typeof(sbyte), typeof(SByteField));
|
||||
AddDrawer(typeof(decimal), typeof(DecimalField));
|
||||
AddDrawer(typeof(Quaternion), typeof(QuaternionField));
|
||||
AddDrawer(typeof(LayerMask), typeof(LayerMaskField));
|
||||
AddDrawer(typeof(VRCUrl), typeof(VRCUrlField));
|
||||
AddDrawer(typeof(MirrorClearFlags), typeof(MirrorReflectionClearFlagsField));
|
||||
AddDrawer(typeof(VideoError), typeof(VideoErrorField));
|
||||
}
|
||||
|
||||
static void AddDrawer(Type fieldType, Type drawerType)
|
||||
{
|
||||
var iNotifyType = typeof(INotifyValueChanged<>).MakeGenericType(fieldType);
|
||||
|
||||
if (!iNotifyType.IsAssignableFrom(drawerType))
|
||||
{
|
||||
Debug.LogWarning("The custom field drawer " + drawerType +
|
||||
" does not implements INotifyValueChanged< " + fieldType + " >");
|
||||
return;
|
||||
}
|
||||
|
||||
fieldDrawers[fieldType] = drawerType;
|
||||
}
|
||||
|
||||
public static INotifyValueChanged<T> CreateField<T>(T value)
|
||||
{
|
||||
return CreateField(value != null ? value.GetType() : typeof(T)) as INotifyValueChanged<T>;
|
||||
}
|
||||
|
||||
public static VisualElement CreateField(Type t)
|
||||
{
|
||||
Type drawerType;
|
||||
|
||||
fieldDrawers.TryGetValue(t, out drawerType);
|
||||
|
||||
if (drawerType == null)
|
||||
drawerType = fieldDrawers.FirstOrDefault(kp => kp.Key.IsReallyAssignableFrom(t)).Value;
|
||||
|
||||
if (drawerType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
object field;
|
||||
|
||||
if (drawerType == typeof(EnumField))
|
||||
{
|
||||
field = new EnumField(Activator.CreateInstance(t) as Enum);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
field = Activator.CreateInstance(drawerType,
|
||||
BindingFlags.CreateInstance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.OptionalParamBinding, null,
|
||||
new object[] {Type.Missing}, CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
field = Activator.CreateInstance(drawerType,
|
||||
BindingFlags.CreateInstance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.OptionalParamBinding, null,
|
||||
new object[] { }, CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
// For mutiline
|
||||
if (field is TextField textField)
|
||||
textField.multiline = true;
|
||||
|
||||
return field as VisualElement;
|
||||
}
|
||||
|
||||
public static INotifyValueChanged<T> CreateFieldSpecific<T>(T value, Action<object> onValueChanged)
|
||||
{
|
||||
var fieldDrawer = CreateField<T>(value);
|
||||
|
||||
if (fieldDrawer == null)
|
||||
return null;
|
||||
|
||||
fieldDrawer.value = value;
|
||||
if (onValueChanged != null)
|
||||
{
|
||||
fieldDrawer.RegisterValueChangedCallback(
|
||||
(e) => onValueChanged(e.newValue));
|
||||
}
|
||||
|
||||
return fieldDrawer as INotifyValueChanged<T>;
|
||||
}
|
||||
|
||||
public static VisualElement CreateField(Type fieldType, object value, Action<object> onValueChanged)
|
||||
{
|
||||
// Todo: see if we can remove this assignment altogether. Do we actually draw any other enum types?
|
||||
if (typeof(Enum).IsAssignableFrom(fieldType) && !customEnumTypes.Contains(fieldType))
|
||||
fieldType = typeof(Enum);
|
||||
|
||||
if (fieldType.IsArray)
|
||||
{
|
||||
return new UdonArrayEditor(fieldType, onValueChanged, value);
|
||||
}
|
||||
|
||||
VisualElement field = null;
|
||||
|
||||
try
|
||||
{
|
||||
var createFieldSpecificMethod = createFieldMethod.MakeGenericMethod(fieldType);
|
||||
field = createFieldSpecificMethod.Invoke(null, new object[] {value, onValueChanged}) as VisualElement;
|
||||
|
||||
// delay textFields
|
||||
if (field is TextField) ((TextField) field).isDelayed = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfca99fb2e099d84da6c504cd521aa70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9214873dab0ea8a4b91861cd5a04dae3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram
|
||||
{
|
||||
[Serializable]
|
||||
public class UdonGraphElementData
|
||||
{
|
||||
public UdonGraphElementType type;
|
||||
public string uid;
|
||||
public string jsonData;
|
||||
|
||||
public UdonGraphElementData(UdonGraphElementType type, string uid, string jsonData)
|
||||
{
|
||||
this.type = type;
|
||||
this.jsonData = jsonData;
|
||||
this.uid = uid;
|
||||
}
|
||||
}
|
||||
|
||||
public enum UdonGraphElementType
|
||||
{
|
||||
GraphElement,
|
||||
UdonStackNode,
|
||||
UdonGroup,
|
||||
UdonComment,
|
||||
Minimap,
|
||||
VariablesWindow,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f166d8f1c152ef34899019ab9a4fd0f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,144 @@
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
#else
|
||||
using UnityEngine.Experimental.UIElements;
|
||||
using UnityEditor.Experimental.UIElements;
|
||||
#endif
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
|
||||
{
|
||||
public class UdonGraphStatus : ToolbarToggle
|
||||
{
|
||||
private VisualElement _detailsContainer;
|
||||
private UdonProgramSourceView _programSourceView;
|
||||
private UdonGraphProgramAsset _graphAsset;
|
||||
private TextElement _label;
|
||||
|
||||
public UdonGraphStatus(VisualElement detailsContainer)
|
||||
{
|
||||
_detailsContainer = detailsContainer;
|
||||
|
||||
_label = new TextElement() {text = "-", name = "Content"};
|
||||
_label.StretchToParentSize();
|
||||
Add(_label);
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
this.RegisterValueChangedCallback(ShowGraphAssetDetails);
|
||||
#else
|
||||
OnValueChanged(ShowGraphAssetDetails);
|
||||
#endif
|
||||
|
||||
_programSourceView = new UdonProgramSourceView();
|
||||
}
|
||||
|
||||
private void ShowGraphAssetDetails(ChangeEvent<bool> changeEvent)
|
||||
{
|
||||
// Just remove it
|
||||
if (!changeEvent.newValue)
|
||||
{
|
||||
RemoveIfContaining(_programSourceView);
|
||||
return;
|
||||
}
|
||||
|
||||
// else we need to make a new one and show it
|
||||
if (_graphAsset == null)
|
||||
{
|
||||
Debug.LogError("Can't show Asset Details for a null asset.");
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveIfContaining(_programSourceView);
|
||||
_detailsContainer.Add(_programSourceView);
|
||||
_programSourceView.BringToFront();
|
||||
}
|
||||
|
||||
private void RemoveIfContaining(VisualElement element)
|
||||
{
|
||||
if (_detailsContainer.Contains(element))
|
||||
{
|
||||
_detailsContainer.Remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAssemble(bool success, string assembly)
|
||||
{
|
||||
if (!enabledInHierarchy || _label == null) return;
|
||||
|
||||
string newText;
|
||||
Color flashColor;
|
||||
Color targetColor;
|
||||
|
||||
// change visuals based on success
|
||||
if (success)
|
||||
{
|
||||
newText = "OK";
|
||||
flashColor = new Color(0, 1, 0);
|
||||
targetColor = new Color(0.1f, 0.25f, 0.1f, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
newText = "!";
|
||||
flashColor = new Color(1, 0, 0);
|
||||
targetColor = new Color(0.25f, 0.1f, 0.1f, alpha);
|
||||
}
|
||||
|
||||
// Update visuals
|
||||
_label.text = newText;
|
||||
_label.style.backgroundColor = flashColor;
|
||||
LerpColor(targetColor);
|
||||
_programSourceView.SetText(assembly);
|
||||
}
|
||||
|
||||
private Color targetColor;
|
||||
private Color startColor;
|
||||
private float lerpAmount = 0f;
|
||||
private float alpha = 0.75f;
|
||||
|
||||
private void LerpColor(Color newColor)
|
||||
{
|
||||
startColor = _label.style.backgroundColor.value;
|
||||
targetColor = newColor;
|
||||
lerpAmount = 0f;
|
||||
EditorApplication.update += UpdateColor;
|
||||
}
|
||||
|
||||
private void UpdateColor()
|
||||
{
|
||||
lerpAmount += 0.01f;
|
||||
|
||||
if (lerpAmount >= 1)
|
||||
{
|
||||
EditorApplication.update -= UpdateColor;
|
||||
}
|
||||
|
||||
if (_label != null)
|
||||
{
|
||||
_label.style.backgroundColor = Color.Lerp(startColor, targetColor, lerpAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Blur()
|
||||
{
|
||||
EditorApplication.update -= UpdateColor;
|
||||
}
|
||||
|
||||
~UdonGraphStatus()
|
||||
{
|
||||
EditorApplication.update -= UpdateColor;
|
||||
if (_graphAsset != null)
|
||||
{
|
||||
_graphAsset.OnAssemble -= OnAssemble;
|
||||
}
|
||||
}
|
||||
|
||||
internal void LoadAsset(UdonGraphProgramAsset asset)
|
||||
{
|
||||
_graphAsset = asset;
|
||||
_graphAsset.OnAssemble += OnAssemble;
|
||||
_programSourceView.LoadAsset(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54dd824c6c614b94183d92710efe4f5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.Core;
|
||||
|
||||
namespace VRC.Udon.Editor.ProgramSources.UdonGraphProgram.UI.GraphView
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
private const string UseNeonStyleString = "UdonGraphViewSettings.UseNeonStyle";
|
||||
private const string SearchOnSelectedNodeRegistryString = "UdonGraphViewSettings.SearchOnSelectedNodeRegistry";
|
||||
private const string GridSnapSizeString = "UdonGraphViewSettings.GridSnapSize";
|
||||
private const string SearchOnNoodleDropString = "UdonGraphViewSettings.SearchOnNoodleDrop";
|
||||
private const string HighlightFlowString = "UdonGraphViewSettings.HighlightFlow";
|
||||
private const string OpenGraphsString = "UdonGraphViewSettings.OpenGraphs";
|
||||
private const string LastGraphIndexString = "UdonGraphViewSetting.LastGraphIndex";
|
||||
|
||||
[Serializable]
|
||||
public class GraphSettings
|
||||
{
|
||||
public string uid;
|
||||
public string scenePath;
|
||||
public string assetPath;
|
||||
public UdonGraphProgramAsset programAsset;
|
||||
|
||||
public static bool IsValid(GraphSettings graphSettings)
|
||||
{
|
||||
if (graphSettings.uid == null) return true; // unsaved, freshly instantiated graph (for testing)
|
||||
if (graphSettings.programAsset == null) return false;
|
||||
// only one of these needs to be set
|
||||
return graphSettings.scenePath != null || graphSettings.assetPath != null;
|
||||
}
|
||||
|
||||
public static GraphSettings Create(UdonGraphProgramAsset programAsset, UdonBehaviour programBehaviour = null)
|
||||
{
|
||||
var graphSettings = new GraphSettings(programAsset, programBehaviour);
|
||||
|
||||
// return null if the graph settings are invalid
|
||||
return !IsValid(graphSettings) ? null :
|
||||
// graph was valid, return it
|
||||
graphSettings;
|
||||
}
|
||||
|
||||
private GraphSettings(UdonGraphProgramAsset programAsset, UdonBehaviour programBehaviour = null)
|
||||
{
|
||||
this.programAsset = programAsset;
|
||||
|
||||
// Store GUID for this asset to settings for easy reload later
|
||||
if(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(programAsset, out string guid, out long _))
|
||||
{
|
||||
uid = guid;
|
||||
}
|
||||
|
||||
if (programBehaviour == null) // If no udonBehaviour is provided, we're opening a graph asset directly
|
||||
{
|
||||
assetPath = AssetDatabase.GetAssetPath(programAsset);
|
||||
scenePath = "";
|
||||
this.programAsset = programAsset;
|
||||
}
|
||||
else
|
||||
{
|
||||
assetPath = VRC.Core.ExtensionMethods.GetHierarchyPath(programBehaviour.transform);
|
||||
scenePath = programBehaviour.gameObject.scene.path;
|
||||
this.programAsset = programAsset;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GraphSettingsList
|
||||
{
|
||||
public GraphSettings[] graphSettingsArray;
|
||||
|
||||
public static List<GraphSettings> GetGraphSettings()
|
||||
{
|
||||
return DeserializeGraphs(PlayerPrefs.GetString(OpenGraphsString));
|
||||
}
|
||||
|
||||
public static void SetGraphSettings(IEnumerable<GraphSettings> graphSettings)
|
||||
{
|
||||
PlayerPrefs.SetString(OpenGraphsString, SerializeGraphs(graphSettings));
|
||||
}
|
||||
|
||||
private GraphSettingsList(IEnumerable<GraphSettings> graphSettingsList)
|
||||
{
|
||||
graphSettingsArray = graphSettingsList.ToArray();
|
||||
}
|
||||
|
||||
private static string SerializeGraphs(IEnumerable<GraphSettings> graphs)
|
||||
{
|
||||
GraphSettingsList graphSettingsList = new GraphSettingsList(graphs);
|
||||
return JsonUtility.ToJson(graphSettingsList);
|
||||
}
|
||||
|
||||
private static List<GraphSettings> DeserializeGraphs(string value)
|
||||
{
|
||||
GraphSettingsList data = JsonUtility.FromJson<GraphSettingsList>(value) ?? new GraphSettingsList(Array.Empty<GraphSettings>());
|
||||
return new List<GraphSettings>(data.graphSettingsArray);
|
||||
}
|
||||
}
|
||||
|
||||
// For Testing
|
||||
public static void Reset()
|
||||
{
|
||||
PlayerPrefs.DeleteKey(UseNeonStyleString);
|
||||
PlayerPrefs.DeleteKey(SearchOnSelectedNodeRegistryString);
|
||||
PlayerPrefs.DeleteKey(GridSnapSizeString);
|
||||
PlayerPrefs.DeleteKey(SearchOnNoodleDropString);
|
||||
PlayerPrefs.DeleteKey(HighlightFlowString);
|
||||
PlayerPrefs.DeleteKey(OpenGraphsString);
|
||||
PlayerPrefs.DeleteKey(LastGraphIndexString);
|
||||
GraphSettingsList.SetGraphSettings(Array.Empty<GraphSettings>());
|
||||
}
|
||||
|
||||
public static void CleanSerializedData()
|
||||
{
|
||||
List<GraphSettings> graphSettingsList = GraphSettingsList.GetGraphSettings();
|
||||
for (int index = graphSettingsList.Count - 1; index >= 0; index--)
|
||||
{
|
||||
GraphSettings openGraph = graphSettingsList[index];
|
||||
if (!GraphSettings.IsValid(openGraph))
|
||||
{
|
||||
graphSettingsList.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
GraphSettingsList.SetGraphSettings(graphSettingsList);
|
||||
}
|
||||
|
||||
public static bool UseNeonStyle
|
||||
{
|
||||
get => PlayerPrefs.GetInt(UseNeonStyleString, 0) == 1;
|
||||
set => PlayerPrefs.SetInt(UseNeonStyleString, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public static int LastGraphIndex
|
||||
{
|
||||
get => PlayerPrefs.GetInt(LastGraphIndexString, 0);
|
||||
set => PlayerPrefs.SetInt(LastGraphIndexString, value);
|
||||
}
|
||||
|
||||
public static GraphSettings GetGraph(string graphName)
|
||||
{
|
||||
List<GraphSettings> openGraphList = GraphSettingsList.GetGraphSettings();
|
||||
for (int index = openGraphList.Count - 1; index >= 0; index--)
|
||||
{
|
||||
GraphSettings t = openGraphList[index];
|
||||
if (t.programAsset == null) continue;
|
||||
if (t.programAsset.name != graphName) continue;
|
||||
return t;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GraphSettings GetLastGraph()
|
||||
{
|
||||
// Fetch Open Graphs from Persistant Storage
|
||||
var openGraphList = GraphSettingsList.GetGraphSettings();
|
||||
int lastGraphIndex = LastGraphIndex;
|
||||
if (lastGraphIndex < 0 || lastGraphIndex >= openGraphList.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return openGraphList[LastGraphIndex];
|
||||
}
|
||||
|
||||
public static void SetLastGraph(UdonGraphProgramAsset programAsset, UdonBehaviour programBehaviour = null)
|
||||
{
|
||||
GraphSettings graphSettings = GraphSettings.Create(programAsset, programBehaviour);
|
||||
if (graphSettings != null)
|
||||
{
|
||||
SetLastGraph(graphSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLastGraph(GraphSettings graphSettings)
|
||||
{
|
||||
var openGraphList = GraphSettingsList.GetGraphSettings();
|
||||
|
||||
// flag to track if we've found the graph we're trying to open
|
||||
// in the opened graphs list
|
||||
bool isOpen = false;
|
||||
|
||||
for (int i = 0; i < openGraphList.Count; i++)
|
||||
{
|
||||
GraphSettings graph = openGraphList[i];
|
||||
if (graph.uid != graphSettings.uid) continue;
|
||||
// Graph was open already, update it
|
||||
LastGraphIndex = i;
|
||||
openGraphList[i] = graphSettings;
|
||||
isOpen = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isOpen)
|
||||
{
|
||||
// Open the graph
|
||||
openGraphList.Add(graphSettings);
|
||||
LastGraphIndex = openGraphList.Count - 1;
|
||||
}
|
||||
// Update Persistant Data
|
||||
GraphSettingsList.SetGraphSettings(openGraphList);
|
||||
}
|
||||
|
||||
public static void CloseGraph(string graphName)
|
||||
{
|
||||
var graphSettings = GetGraph(graphName);
|
||||
if (graphSettings == null) return;
|
||||
CloseGraph(graphSettings);
|
||||
}
|
||||
|
||||
private static void CloseGraph(GraphSettings graphSettings)
|
||||
{
|
||||
// stash the last graph
|
||||
var lastGraph = GetLastGraph();
|
||||
|
||||
// Fetch Open Graphs from Persistant Storage
|
||||
var openGraphList = GraphSettingsList.GetGraphSettings();
|
||||
|
||||
// Remove the graph from the list
|
||||
for (int index = openGraphList.Count - 1; index >= 0; index--)
|
||||
{
|
||||
GraphSettings setting = openGraphList[index];
|
||||
if (setting.uid == graphSettings.uid)
|
||||
openGraphList.RemoveAt(index);
|
||||
}
|
||||
|
||||
// restore the last graph index after mutating list
|
||||
LastGraphIndex = openGraphList.LastIndexOf(lastGraph);
|
||||
if(LastGraphIndex < 0)
|
||||
LastGraphIndex = openGraphList.Count - 1;
|
||||
|
||||
// Update Persistant Data
|
||||
GraphSettingsList.SetGraphSettings(openGraphList);
|
||||
}
|
||||
|
||||
public static bool SearchOnSelectedNodeRegistry
|
||||
{
|
||||
get => PlayerPrefs.GetInt(SearchOnSelectedNodeRegistryString, 1) == 1;
|
||||
set => PlayerPrefs.SetInt(SearchOnSelectedNodeRegistryString, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public static int GridSnapSize
|
||||
{
|
||||
get => PlayerPrefs.GetInt(GridSnapSizeString, 0);
|
||||
set => PlayerPrefs.SetInt(GridSnapSizeString, value);
|
||||
}
|
||||
|
||||
public static bool SearchOnNoodleDrop
|
||||
{
|
||||
get => PlayerPrefs.GetInt(SearchOnNoodleDropString, 1) == 1;
|
||||
set => PlayerPrefs.SetInt(SearchOnNoodleDropString, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public static bool HighlightFlow
|
||||
{
|
||||
get => PlayerPrefs.GetInt(HighlightFlowString, 0) == 1;
|
||||
set => PlayerPrefs.SetInt(HighlightFlowString, value ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87e2044d3bcb715499ac68cc7380a9ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user