Esempio n. 1
0
        private void AddToFieldOrder(UTField field, SerializedProperty prop,
                                     ref Dictionary <string, UTFieldType> targetObj)
        {
            // we reuse ListView rendering for the arrays
            if (field.isArray && !field.isInListView)
            {
                listPaginations.Add(prop.displayName, 0);
                utilsShown.Add(prop.displayName, false);
                listViews.Add(prop.displayName, new List <UTField> {
                    field
                });
                targetObj.Add(prop.displayName, UTFieldType.ListView);
                return;
            }

            if (field.isInListView)
            {
                if (!listViews.ContainsKey(field.listViewName))
                {
                    utilsShown.Add(field.listViewName, false);
                    listPaginations.Add(field.listViewName, 0);
                    listViews.Add(field.listViewName, new List <UTField> {
                        field
                    });
                    targetObj.Add(field.listViewName, UTFieldType.ListView);
                }
                else
                {
                    listViews[field.listViewName].Add(field);
                }

                return;
            }

            if (field.isInHorizontal)
            {
                if (!horizontalViews.ContainsKey(field.horizontalName))
                {
                    horizontalViews.Add(field.horizontalName, new List <UTField> {
                        field
                    });
                    targetObj.Add(field.horizontalName, UTFieldType.Horizontal);
                }
                else
                {
                    horizontalViews[field.horizontalName].Add(field);
                }

                return;
            }
            else
            {
                targetObj.Add(field.name, UTFieldType.Regular);
            }
        }
Esempio n. 2
0
        private void AddToFieldOrder(UTField field, SerializedProperty prop, bool addToFoldout = false,
                                     bool addToTabGroup = false)
        {
            if (!addToFoldout && !addToTabGroup)
            {
                AddToFieldOrder(field, prop, ref fieldOrder);
                return;
            }

            if (addToFoldout)
            {
                var foldoutTarget = foldouts[field.foldoutName];
                AddToFieldOrder(field, prop, ref foldoutTarget);
                foldouts[field.foldoutName] = foldoutTarget;
                return;
            }

            var tabGroupTarget = tabs[field.tabGroupName];

            AddToFieldOrder(field, prop, ref tabGroupTarget);
            tabs[field.tabGroupName] = tabGroupTarget;
            return;
        }
Esempio n. 3
0
        public override void OnInspectorGUI()
        {
            t = (UdonSharpBehaviour)target;

            showUdonSettings = (bool)(UTUtils.GetUTSetting("showUdonSettings", UTUtils.UTSettingType.Bool) ?? false);

            // we force-disable collision transfer for toolkit driven behaviours as it is not applicable
            if (!nonUBChecked)
            {
                nonUBMode    = t.gameObject.GetComponent <UdonBehaviour>() == null;
                nonUBChecked = true;
            }

            if (!nonUBMode)
            {
                if (UdonSharpEditorUtility.GetBackingUdonBehaviour(t).AllowCollisionOwnershipTransfer)
                {
                    UdonSharpEditorUtility.GetBackingUdonBehaviour(t).AllowCollisionOwnershipTransfer = false;
                }
            }

            var headerExited = false;

            EditorGUI.BeginChangeCheck();
            showUdonSettings = GUILayout.Toggle(showUdonSettings, "Udon Settings", UTStyles.smallButton);
            if (EditorGUI.EndChangeCheck())
            {
                UTUtils.SetUTSetting("showUdonSettings", UTUtils.UTSettingType.Bool, showUdonSettings);
            }

            if (showUdonSettings)
            {
                headerExited = UdonSharpGUI.DrawDefaultUdonSharpBehaviourHeader(t, true);
            }

            if (headerExited)
            {
                return;
            }

            #region Caching

            if (!cacheBuilt)
            {
                tT           = t.GetType();
                programAsset = UdonSharpEditorUtility.GetUdonSharpProgramAsset(t);
                behInfo      = new UTBehaviourInfo(t);

                var prop = serializedObject.GetIterator();
                var next = prop.NextVisible(true);

                if (next)
                {
                    do
                    {
                        if (prop.name == "m_Script")
                        {
                            continue;
                        }

                        if (!fieldCache.ContainsKey(prop.name))
                        {
                            var newField = new UTField(prop);
                            fieldCache.Add(prop.name, newField);
                            if (newField.isInTabGroup)
                            {
                                if (!tabs.ContainsKey(newField.tabGroupName))
                                {
                                    tabs.Add(newField.tabGroupName, new Dictionary <string, UTFieldType>());
                                    // we only support one tab group per behaviour right now
                                    if (!tabsExist)
                                    {
                                        fieldOrder.Add(newField.tabGroupName, UTFieldType.Tab);
                                        tabsExist = true;
                                    }
                                }

                                // since tabs can host foldouts - we need to explicitly retarget them
                                // this logic could be generalized if i ever want to make all of this recursive
                                if (newField.isInFoldout)
                                {
                                    if (!foldouts.ContainsKey(newField.foldoutName))
                                    {
                                        foldouts.Add(newField.foldoutName, new Dictionary <string, UTFieldType>());
                                        tabs[newField.tabGroupName].Add(newField.foldoutName, UTFieldType.Foldout);
                                    }

                                    AddToFieldOrder(newField, prop, true);
                                    continue;
                                }

                                AddToFieldOrder(newField, prop, addToTabGroup: true);
                                continue;
                            }

                            if (newField.isInFoldout)
                            {
                                if (!foldouts.ContainsKey(newField.foldoutName))
                                {
                                    foldouts.Add(newField.foldoutName, new Dictionary <string, UTFieldType>());
                                    fieldOrder.Add(newField.foldoutName, UTFieldType.Foldout);
                                }

                                AddToFieldOrder(newField, prop, true);
                                continue;
                            }

                            AddToFieldOrder(newField, prop);
                        }
                    } while (prop.NextVisible(false));
                }

                cacheBuilt = true;
                return;
            }

            var e = Event.current;
            if (e.type == EventType.Repaint)
            {
                if (firstRepaint)
                {
                    firstRepaint = false;
                    return;
                }
            }

            #endregion

            if (behInfo.customName != null || behInfo.helpUrl != null)
            {
                EditorGUILayout.BeginHorizontal();
                UTStyles.RenderHeader(behInfo.customName != null ? behInfo.customName : behInfo.name);
                if (behInfo.helpUrl != null)
                {
                    if (GUILayout.Button("?", GUILayout.Height(26), GUILayout.Width(26)))
                    {
                        Application.OpenURL(behInfo.helpUrl);
                    }
                }
                EditorGUILayout.EndHorizontal();
            }

            if (behInfo.helpMsg != null)
            {
                UTStyles.RenderNote(behInfo.helpMsg);
            }

            if (behInfo.onBeforeEditor != null)
            {
                behInfo.onBeforeEditor.Invoke(t, new object[] { serializedObject });
            }

            // handle jagged arrays
            Undo.RecordObject(t, "Change Properties");

            EditorGUI.BeginChangeCheck();
            droppedObjects = false;

            // this method is overrideable by custom code
            DrawGUI();

            if (EditorGUI.EndChangeCheck() || droppedObjects || shouldReserialize)
            {
                if (behInfo.onValuesChanged != null)
                {
                    behInfo.onValuesChanged.Invoke(t, new object[] { serializedObject });
                }
                serializedObject.ApplyModifiedProperties();
            }

            if (droppedObjects)
            {
                return;
            }

            if (behInfo.onAfterEditor != null)
            {
                behInfo.onAfterEditor.Invoke(t, new object[] { serializedObject });
            }

            #region Buttons
            if (behInfo.buttons != null && !Application.isPlaying && behInfo.buttons.Length > 0)
            {
                buttonsExpanded = UTStyles.FoldoutHeader("Editor Methods", buttonsExpanded);
                if (buttonsExpanded)
                {
                    EditorGUILayout.BeginVertical(new GUIStyle("helpBox"));
                    foreach (var button in behInfo.buttons)
                    {
                        if (GUILayout.Button(button.Name))
                        {
                            button.Invoke(t, new object[] {});
                        }
                    }
                    EditorGUILayout.EndVertical();
                }
            }
            if (Application.isPlaying && behInfo.udonCustomEvents.Length > 0 && !nonUBMode)
            {
                methodsExpanded = UTStyles.FoldoutHeader("Udon Events", methodsExpanded);
                if (methodsExpanded)
                {
                    EditorGUILayout.BeginVertical(new GUIStyle("helpBox"));
                    var rowBreak = Mathf.Max(1, Mathf.Min(3, behInfo.udonCustomEvents.Length - 1));
                    var rowEndI  = -100;
                    foreach (var(button, i) in behInfo.udonCustomEvents.WithIndex())
                    {
                        if (i == rowEndI && i != behInfo.udonCustomEvents.Length - 1)
                        {
                            EditorGUILayout.EndHorizontal();
                        }
                        if (i % rowBreak == 0 && i != behInfo.udonCustomEvents.Length - 1)
                        {
                            EditorGUILayout.BeginHorizontal();
                            rowEndI = Math.Min(i + rowBreak, behInfo.udonCustomEvents.Length - 1);
                        }
                        if (GUILayout.Button(button))
                        {
                            UdonSharpEditorUtility.GetBackingUdonBehaviour(t).SendCustomEvent(button);
                            UdonSharpEditorUtility.CopyUdonToProxy(t);
                        }
                        if (i == behInfo.udonCustomEvents.Length - 1 && rowEndI != -100)
                        {
                            EditorGUILayout.EndHorizontal();
                        }
                    }
                    EditorGUILayout.EndVertical();
                }
            }
            #endregion
        }
        private void HandleFieldChangeArray(UTField field, SerializedProperty prop, int arrIndex = 0)
        {
            if (field.isValueChangeAtomic)
            {
                if (!field.isInListView)
                {
                    field.onValueChaged.Invoke(t, new object[] { prop, arrIndex });
                    return;
                }

                var fields = listViews[field.listViewName];
                var props  = new List <SerializedProperty>();
                foreach (var utField in fields)
                {
                    var parentProp = serializedObject.FindProperty(utField.name);
                    props.Add(parentProp);
                }

                field.onValueChaged.Invoke(t, props.ToArray());
                return;
            }

            if (field.isValueChangedFull)
            {
                if (!field.isInListView)
                {
                    var parentProp = serializedObject.FindProperty(field.name);
                    var props      = new SerializedProperty[parentProp.arraySize];
                    for (int i = 0; i < parentProp.arraySize; i++)
                    {
                        props[i] = parentProp.GetArrayElementAtIndex(i);
                    }

                    field.onValueChaged.Invoke(t, new object[] { props });
                    return;
                }

                {
                    var fields = listViews[field.listViewName];
                    var props  = new List <SerializedProperty[]>();
                    foreach (var utField in fields)
                    {
                        var parentProp = serializedObject.FindProperty(utField.name);
                        var localProps = new List <SerializedProperty>();
                        for (int i = 0; i < parentProp.arraySize; i++)
                        {
                            localProps.Add(parentProp.GetArrayElementAtIndex(i));
                        }

                        props.Add(localProps.ToArray());
                    }

                    field.onValueChaged.Invoke(t, props.ToArray());
                    return;
                }
            }

            if (field.isValueChangedWithObject)
            {
                if (!field.isInListView)
                {
                    var parentProp = serializedObject.FindProperty(field.name);
                    var props      = new SerializedProperty[parentProp.arraySize];
                    for (int i = 0; i < parentProp.arraySize; i++)
                    {
                        props[i] = parentProp.GetArrayElementAtIndex(i);
                    }

                    field.onValueChaged.Invoke(t, new object[] { serializedObject, props });
                    return;
                }

                {
                    var fields = listViews[field.listViewName];
                    var props  = new List <SerializedProperty[]>();
                    foreach (var utField in fields)
                    {
                        var parentProp = serializedObject.FindProperty(utField.name);
                        var localProps = new List <SerializedProperty>();
                        for (int i = 0; i < parentProp.arraySize; i++)
                        {
                            localProps.Add(parentProp.GetArrayElementAtIndex(i));
                        }

                        props.Add(localProps.ToArray());
                    }

                    var appended = new List <object> {
                        serializedObject
                    };
                    appended.AddRange(props);

                    field.onValueChaged.Invoke(t, appended.ToArray());
                    return;
                }
            }
        }
        private void DrawFieldElement(UTField field, SerializedProperty prop)
        {
            var propPath = prop.propertyPath;
            var arrIndex = Convert.ToInt32(field.isArray
        ? propPath.Substring(propPath.LastIndexOf("[") + 1, propPath.LastIndexOf("]") - propPath.LastIndexOf("[") - 1)
        : null);
            var customLabel = field.attributes.Find(i => i is ShowLabelAttribute) as ShowLabelAttribute;
            var uiAttrs     = field.uiAttrs;
            var uiOverride  = uiAttrs.Where(i => i.GetType().GetMethod("OnGUI")?.DeclaringType == i.GetType()).ToArray();

            EditorGUI.BeginChangeCheck();
            switch (prop.type)
            {
            case "bool":
                if (uiOverride.Any())
                {
                    uiOverride.First().OnGUI(prop);
                    break;
                }

                if (customLabel == null)
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent(), GUILayout.MaxWidth(30));
                }
                else
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent(customLabel.label ?? prop.displayName));
                }

                break;

            default:
                // for list views we handle the UI separately due to how popup targeting works
                if (uiOverride.Any() && !field.isInListView)
                {
                    uiOverride.First().OnGUI(prop);
                    break;
                }

                var popupAttr = field.attributes.Find(i => i is PopupAttribute) as PopupAttribute;
                if (popupAttr != null)
                {
                    var sourceProp = UTUtils.GetPropThroughAttribute(serializedObject, popupAttr.methodName);
                    // I do not like this handling
                    // need a way to determine if target is a part of the list view or not without breaking the bank
                    var source = sourceProp == null ? null : !field.isInListView
              ? sourceProp
              : sourceProp.isArray && sourceProp.arraySize > arrIndex
                ? sourceProp.GetArrayElementAtIndex(arrIndex)
                : null;

                    var options = UTUtils.GetPopupOptions(prop, source, popupAttr, out var selectedIndex);
                    selectedIndex = EditorGUILayout.Popup(selectedIndex, options);
                    // we force reserialize for cases of default values
                    if (prop.type == "int")
                    {
                        if (prop.intValue != selectedIndex)
                        {
                            shouldReserialize = true;
                        }
                        prop.intValue = selectedIndex;
                    }
                    else
                    {
                        if (prop.stringValue != options[selectedIndex])
                        {
                            shouldReserialize = true;
                        }
                        prop.stringValue = options[selectedIndex];
                    }
                    break;
                }

                // if there are no popup attributes - still allow gui override
                if (uiOverride.Any(i => !(i is PopupAttribute)))
                {
                    uiOverride.First(i => !(i is PopupAttribute)).OnGUI(prop);
                    break;
                }

                if (customLabel == null)
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent());
                }
                else
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent(customLabel.label ?? prop.displayName));
                }

                break;
            }

            if (EditorGUI.EndChangeCheck() && field.onValueChaged != null)
            {
                if (!field.isInListView)
                {
                    field.onValueChaged.Invoke(t, new object[] { prop });
                    return;
                }
                HandleFieldChangeArray(field, prop, arrIndex);
            }
        }
        private void DrawField(UTField field, SerializedProperty prop)
        {
            // this is taken directly from U# code
            var    isNonDefault = false;
            object origValue    = null;

            if (programAsset && !nonUBMode)
            {
                origValue = programAsset.GetRealProgram().Heap.GetHeapVariable(programAsset.GetRealProgram().SymbolTable.GetAddressFromSymbol(prop.name));
                var fieldVal = tT.GetField(prop.name)?.GetValue(t);
                isNonDefault = fieldVal != null && origValue != null && !origValue.Equals(fieldVal);
            }

            var uiAttrs = field.uiAttrs;

            if (!uiAttrs.Any())
            {
                EditorGUI.BeginDisabledGroup(field.isDisabled);
                if (isNonDefault)
                {
                    PropertyFieldWithUndo(prop, origValue);
                }
                else
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent(prop.displayName));
                }
                EditorGUI.EndDisabledGroup();
                return;
            }

            var isVisible = true;

            foreach (var uiAttr in uiAttrs)
            {
                if (!uiAttr.GetVisible(prop))
                {
                    isVisible = false;
                    break;
                }
            }

            if (!isVisible)
            {
                return;
            }

            foreach (var uiAttr in uiAttrs)
            {
                uiAttr.BeforeGUI(prop);
            }

            EditorGUI.BeginChangeCheck();
            EditorGUI.BeginDisabledGroup(field.isDisabled);
            // we can only have a single UI overriding OnGUI that renders the actual prop field
            var uiOverride = uiAttrs.Where(i => i.GetType().GetMethod("OnGUI")?.DeclaringType == i.GetType()).ToArray();

            if (uiOverride.Any())
            {
                uiOverride.First().OnGUI(prop);
            }
            else
            {
                if (isNonDefault)
                {
                    PropertyFieldWithUndo(prop, origValue);
                }
                else
                {
                    EditorGUILayout.PropertyField(prop, new GUIContent(prop.displayName));
                }
            }

            EditorGUI.EndDisabledGroup();
            if (EditorGUI.EndChangeCheck() && field.onValueChaged != null)
            {
                field.onValueChaged.Invoke(t, new object[] { prop });
            }

            foreach (var uiAttr in uiAttrs)
            {
                uiAttr.AfterGUI(prop);
            }
        }