├── ExtendableEnums ├── Editor │ └── ExtendableEnums.cs └── ExtendEnumAttribute.cs ├── LICENSE └── README.md /ExtendableEnums/Editor/ExtendableEnums.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using UnityEditor; 6 | using System.Reflection; 7 | using System.Linq; 8 | 9 | [CustomPropertyDrawer(typeof(ExtendEnumAttribute))] 10 | public class ExtendEnumDrawer : PropertyDrawer 11 | { 12 | //Some static values to pass back and forth with the popup 13 | static string newValueText = ""; 14 | static SerializedProperty currentProperty = null; 15 | static bool showWindow = false; 16 | static List enumNames; 17 | static int popupWidth = 150; 18 | static int popupHeight = 90; 19 | 20 | //Our class to make the popup 21 | public class NewValuePopup : PopupWindowContent 22 | { 23 | public override void OnGUI(Rect position) 24 | { 25 | EditorGUILayout.LabelField("New Value", EditorStyles.wordWrappedLabel); 26 | 27 | newValueText = GUILayout.TextField(newValueText).Trim(); 28 | 29 | float exitSize = 18; 30 | if (GUI.Button(new Rect(position.width - exitSize, 0, exitSize, exitSize), "X")) 31 | { 32 | this.editorWindow.Close(); 33 | } 34 | GUILayout.BeginHorizontal(); 35 | if (GUILayout.Button("Confirm")) 36 | { 37 | List names = enumNames; 38 | for (int i = 0; i < names.Count; i++) 39 | { 40 | names[i] = names[i].ToLower(); 41 | } 42 | if (!names.Contains(newValueText.ToLower())) 43 | { 44 | //This sends our enum to go get created. Be safe little enum. 45 | FindClassFile(GetEnumName(currentProperty), newValueText); 46 | this.editorWindow.Close(); 47 | } 48 | else 49 | { 50 | newValueText = newValueText + "_Copy"; 51 | } 52 | } 53 | if (GUILayout.Button("Cancel")) 54 | { 55 | this.editorWindow.Close(); 56 | } 57 | GUILayout.EndHorizontal(); 58 | GUIStyle small = new GUIStyle(EditorStyles.wordWrappedLabel); 59 | small.fontSize = 9; 60 | EditorGUILayout.LabelField("(To add multiple, separate with commas)", small); 61 | } 62 | 63 | public override void OnOpen() 64 | { 65 | showWindow = true; 66 | newValueText = ""; 67 | base.OnOpen(); 68 | } 69 | 70 | public override void OnClose() 71 | { 72 | showWindow = false; 73 | base.OnClose(); 74 | } 75 | 76 | public override Vector2 GetWindowSize() 77 | { 78 | return new Vector2(popupWidth, popupHeight); 79 | } 80 | } 81 | 82 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 83 | { 84 | currentProperty = property; 85 | ExtendEnumAttribute source = (ExtendEnumAttribute)attribute; 86 | System.Enum enumVal = GetBaseProperty(property); 87 | 88 | enumNames = (property.enumDisplayNames).OfType().ToList(); 89 | if (source.display) 90 | { 91 | int[] enumValues = (int[])(System.Enum.GetValues(enumVal.GetType())); 92 | for (int i = 0; i < enumNames.Count; i++) 93 | { 94 | enumNames[i] += " | " + enumValues[i]; 95 | } 96 | } 97 | 98 | EditorGUI.BeginProperty(position, label, property); 99 | if (!showWindow) 100 | { 101 | if (enumNames.Count != 0) 102 | { 103 | enumNames.Add("Add New..."); 104 | int newValue = EditorGUI.Popup(position, property.displayName, property.intValue, enumNames.ToArray()); 105 | 106 | if (newValue == enumNames.Count - 1) 107 | { 108 | NewValuePopup popup = new NewValuePopup(); 109 | PopupWindow.Show(new Rect(Screen.width / 2 - popupWidth / 2, position.y - popupHeight / 2, 0, 0), popup); 110 | 111 | newValueText = ""; 112 | } 113 | else 114 | { 115 | property.intValue = newValue; 116 | } 117 | } 118 | else 119 | { 120 | EditorGUI.LabelField(position, "Extendable Enums needs at least one value in your declared enum."); 121 | } 122 | } 123 | else 124 | { 125 | EditorGUI.LabelField(position, "Waiting for new value input."); 126 | } 127 | 128 | EditorGUI.EndProperty(); 129 | } 130 | 131 | //I know this is pretty much the same as GetBaseProperty. 132 | static string GetEnumName(SerializedProperty prop) 133 | { 134 | string[] separatedPaths = prop.propertyPath.Split('.'); 135 | System.Object reflectionTarget = prop.serializedObject.targetObject as object; 136 | 137 | foreach (var path in separatedPaths) 138 | { 139 | FieldInfo fieldInfo = reflectionTarget.GetType().GetField(path, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 140 | string name = fieldInfo.FieldType.Name; 141 | return name; 142 | } 143 | return "Null"; 144 | } 145 | 146 | static T GetBaseProperty(SerializedProperty prop) 147 | { 148 | string[] separatedPaths = prop.propertyPath.Split('.'); 149 | System.Object reflectionTarget = prop.serializedObject.targetObject as object; 150 | 151 | foreach (var path in separatedPaths) 152 | { 153 | FieldInfo fieldInfo = reflectionTarget.GetType().GetField(path, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 154 | reflectionTarget = fieldInfo.GetValue(reflectionTarget); 155 | } 156 | return (T)reflectionTarget; 157 | } 158 | 159 | static void FindClassFile(string enumName, string newEnum) 160 | { 161 | KeyValuePair codeFile = FindAllScriptFiles(Application.dataPath, "enum " + enumName); 162 | if (codeFile.Key != "NOPE") 163 | AddNewEnum(codeFile.Key, codeFile.Value, enumName, newEnum); 164 | else 165 | Debug.LogError("Could not find enum class"); 166 | } 167 | 168 | //Formatting is a little weird on this depending. It works pretty well with a single line enum format, but other than that it can do weird shit. 169 | //But it still works, so it's fine... for now. 170 | static void AddNewEnum(string classFile, string path, string enumName, string newEnum) 171 | { 172 | string[] originalSplit = classFile.Split(new[] { "enum " + enumName }, System.StringSplitOptions.RemoveEmptyEntries); 173 | string newHalf = originalSplit[1]; 174 | string enumSection = newHalf.Split('}')[0]; 175 | string[] commas = enumSection.Split(','); 176 | if (commas.Length == 0 && enumSection.Split('{')[0].Trim().Length == 0) //They've left the enum empty... for some reason. 177 | { 178 | Debug.Log("Uhh idk yet"); 179 | newHalf = newHalf.Replace(enumSection, enumSection + newEnum); 180 | } 181 | else 182 | { 183 | bool commaAfter = commas[commas.Length - 1].Trim().Length == 0; //This should check if they added a comma after their last enum value. 184 | 185 | if (commaAfter) 186 | { 187 | newHalf = newHalf.Replace(enumSection, enumSection + newEnum + ", "); 188 | } 189 | else 190 | { 191 | while (enumSection.Length > 0 && enumSection[enumSection.Length - 1] == ' ') 192 | enumSection = enumSection.Substring(0, enumSection.Length - 1); 193 | newHalf = newHalf.Replace(enumSection, enumSection + ", " + newEnum); 194 | } 195 | } 196 | 197 | string result = classFile.Replace(originalSplit[1], newHalf); 198 | using (var file = File.Open(path, FileMode.Create)) 199 | { 200 | using (var writer = new StreamWriter(file)) 201 | { 202 | writer.Write(result); 203 | } 204 | } 205 | AssetDatabase.Refresh(); 206 | } 207 | 208 | static KeyValuePair FindAllScriptFiles(string startDir, string enumToFind) 209 | { 210 | try 211 | { 212 | foreach (string file in Directory.GetFiles(startDir)) 213 | { 214 | if ((file.Contains(".cs") || file.Contains(".js")) && !file.Contains(".meta")) 215 | { 216 | string current = File.ReadAllText(file); 217 | string currentTrimmed = current.Replace(" ", "").Replace("\n", "").Replace("\t", "").Replace("\r", ""); 218 | if (currentTrimmed.Contains(enumToFind.Replace(" ", "") + "{")) 219 | return new KeyValuePair(current, file); 220 | } 221 | } 222 | foreach (string dir in Directory.GetDirectories(startDir)) 223 | { 224 | KeyValuePair result = FindAllScriptFiles(dir, enumToFind); 225 | if (result.Key != "NOPE") 226 | return result; 227 | } 228 | } 229 | catch (System.Exception ex) 230 | { 231 | Debug.Log(ex.Message); 232 | } 233 | return new KeyValuePair("NOPE", "NOPE"); 234 | } 235 | 236 | 237 | } 238 | -------------------------------------------------------------------------------- /ExtendableEnums/ExtendEnumAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [System.AttributeUsage(System.AttributeTargets.Field)] 6 | public class ExtendEnumAttribute : PropertyAttribute 7 | { 8 | public readonly bool display = true; 9 | public ExtendEnumAttribute(bool displayValues = true) 10 | { 11 | display = displayValues; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cory Koseck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extendable Enums 2 | Hello! Extendable Enums is a simple tool that allows you to add new enum values to the declaration from within your Unity inspector. It's super easy to use, and completely free. 3 | 4 | ## Well, How Do I Use It? 5 | Simple, you'll just add the [ExtendEnum] attribute onto your enum instance, and you're all setup. Next when you go into Unity, hit the drop down for your enum. You'll see another option at the bottom "Add New...", hitting that will open a new popup that will allow you to input a new value. Once you've done so and hit confirm, the editor will have to recompile your code. So depending on the size of your project it may take a moment. 6 | 7 | You can also use [ExtendEnum(false)] to stop the enum values from showing in the inspector. 8 | 9 | ## I'm still confused... Help! 10 | Not to worry, I made a short and simple video. 11 | 12 | [![How To Use Extendable Enums](http://img.youtube.com/vi/ajsY4y5nmNs/0.jpg)](http://www.youtube.com/watch?v=ajsY4y5nmNs) 13 | 14 | ## Support 15 | If you're having any problems using Extendable Enums, or you need to get in touch for any other reason, please email me at: KoseckCory@gmail.com 16 | Also you can check out [my website here](http://corykoseck.com). 17 | --------------------------------------------------------------------------------