Created
January 15, 2024 03:23
-
-
Save wesleywh/b167e0bc001b5c7b378517a87ed0748b to your computer and use it in GitHub Desktop.
Unity Custom Right Click Menu To List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region Editor | |
public static class PCGSettingsCopier | |
{ | |
public static PCGSettings obj = null; | |
public static List<int> indexes = new(); | |
} | |
[CustomEditor(typeof(PCGSettings), true)] | |
public class PCGSettingsEditor : Editor | |
{ | |
#region Properties | |
SerializedProperty itemList; | |
ReorderableList reorderableList; | |
#endregion | |
#region Enable | |
protected virtual void OnEnable() | |
{ | |
itemList = serializedObject.FindProperty("items"); | |
reorderableList = new ReorderableList(serializedObject, itemList, true, true, true, true); | |
reorderableList.multiSelect = true; | |
reorderableList.drawHeaderCallback = rect => { | |
EditorGUI.LabelField(rect, itemList.displayName); | |
}; | |
reorderableList.drawElementCallback += DrawIndividualElement; | |
reorderableList.elementHeightCallback = index => { | |
var element = itemList.GetArrayElementAtIndex(index); | |
return EditorGUI.GetPropertyHeight(element, GUIContent.none, true) + 6f; | |
}; | |
reorderableList.drawElementBackgroundCallback += DrawElementBackground; | |
} | |
protected virtual void DrawElementBackground(Rect rect, int index, bool isActive, bool isFocused) | |
{ | |
var element = itemList.GetArrayElementAtIndex(index); | |
if (isActive) | |
EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.2f)); | |
} | |
protected virtual void DrawIndividualElement(Rect rect, int index, bool isActive, bool isFocused) | |
{ | |
SerializedProperty element = itemList.GetArrayElementAtIndex(index); | |
SerializedProperty nextElement = null; | |
if (index < itemList.arraySize-1) | |
nextElement = itemList.GetArrayElementAtIndex(index+1); | |
rect.y += 2; | |
EditorGUI.indentLevel++; | |
// EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, new GUIContent(element.displayName)); | |
element.isExpanded = EditorGUI.Foldout(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element.isExpanded, new GUIContent(element.displayName)); | |
if (Event.current.type == EventType.ContextClick && rect.Contains(Event.current.mousePosition)) | |
{ | |
if (!isActive) | |
reorderableList.index = index; // Select the index since it wasn't (will override a list of selections if it was) | |
OnContextClick(); | |
} | |
// If the foldout is expanded, display sub-properties | |
if (element.isExpanded) | |
{ | |
EditorGUI.indentLevel++; | |
float row = EditorGUIUtility.singleLineHeight + 2; | |
// Iterate through all child properties of the element | |
SerializedProperty childProperty = element.Copy(); | |
bool enterChildren = true; | |
int count = 1; | |
while (childProperty.NextVisible(enterChildren)) | |
{ | |
if (nextElement != null && childProperty.displayName == nextElement.displayName) break; | |
enterChildren = true; | |
if (childProperty.propertyType == SerializedPropertyType.Vector2 || | |
childProperty.propertyType == SerializedPropertyType.Vector3 || | |
childProperty.propertyType == SerializedPropertyType.Vector4) | |
{ | |
EditorGUI.PropertyField(new Rect(rect.x, rect.y+(row*count), rect.width, EditorGUIUtility.singleLineHeight), childProperty, true); | |
enterChildren = false; | |
count++; | |
} | |
// Exclude the main element property | |
else if (childProperty.name != "Array" && childProperty.name != "data") | |
{ | |
EditorGUI.PropertyField(new Rect(rect.x, rect.y+(row*count), rect.width, EditorGUIUtility.singleLineHeight), childProperty, true); | |
count++; | |
} | |
} | |
enterChildren = false; | |
EditorGUI.indentLevel--; | |
} | |
EditorGUI.indentLevel--; | |
} | |
protected virtual void OnContextClick() | |
{ | |
GenericMenu menu = new GenericMenu(); | |
menu.AddItem(new GUIContent("Delete"), false, Delete); | |
menu.AddItem(new GUIContent(""), false, ()=>{}); | |
menu.AddItem(new GUIContent("Copy"), false, Copy); | |
menu.AddItem(new GUIContent("Paste"), false, Paste); | |
menu.AddItem(new GUIContent(""), false, ()=>{}); | |
if (reorderableList.selectedIndices.Count == 1) | |
{ | |
menu.AddItem(new GUIContent("Duplicate"), false, Duplicate); | |
menu.AddItem(new GUIContent(""), false, ()=>{}); | |
} | |
menu.AddItem(new GUIContent("Set Always Up - True"), false, () => { SetAlwaysUp(true); }); | |
menu.AddItem(new GUIContent("Set Always Up - False"), false, () => { SetAlwaysUp(false); }); | |
menu.ShowAsContext(); | |
Event.current.Use(); | |
} | |
#endregion | |
#region GUI | |
public override void OnInspectorGUI() | |
{ | |
serializedObject.Update(); | |
reorderableList.DoLayoutList(); | |
serializedObject.ApplyModifiedProperties(); | |
} | |
#endregion | |
#region Default Context Actions | |
protected virtual void Copy() | |
{ | |
PCGSettingsCopier.obj = (PCGSettings)target; | |
PCGSettingsCopier.indexes.Clear(); | |
foreach(int index in reorderableList.selectedIndices) | |
PCGSettingsCopier.indexes.Add(index); | |
} | |
protected virtual void Paste() | |
{ | |
List<PCGItem> tmp = new List<PCGItem>(); | |
foreach(int index in PCGSettingsCopier.indexes) | |
tmp.Add(PCGSettingsCopier.obj.items[index]); | |
Undo.RegisterCompleteObjectUndo(target, "Paste Items"); | |
((PCGSettings)target).items.AddRange(tmp); | |
itemList.serializedObject.ApplyModifiedProperties(); | |
itemList.serializedObject.Update(); | |
} | |
protected virtual void Duplicate() | |
{ | |
itemList.GetArrayElementAtIndex(reorderableList.selectedIndices[0]).DuplicateCommand(); | |
itemList.serializedObject.ApplyModifiedProperties(); | |
itemList.serializedObject.Update(); | |
serializedObject.ApplyModifiedProperties(); | |
} | |
protected virtual void Delete() | |
{ | |
for (int i = reorderableList.selectedIndices.Count-1; i > -1; i--) | |
{ | |
itemList.DeleteArrayElementAtIndex(reorderableList.selectedIndices[i]); | |
} | |
serializedObject.ApplyModifiedProperties(); | |
} | |
#endregion | |
#region Custom Context Actions | |
protected virtual void SetAlwaysUp(bool value) | |
{ | |
foreach(int selectedIndex in reorderableList.selectedIndices) | |
itemList.GetArrayElementAtIndex(selectedIndex).FindPropertyRelative("alwaysUp").boolValue = value; | |
serializedObject.ApplyModifiedProperties(); | |
} | |
#endregion | |
} | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Unfortunately unity has no built in way that I can see to make a custom right click item target multiple items in a list. There is a way to target individual items like so:
That will allow you to add a new entry into the existing list, but only allow you to target the selected item.
The above rewrites the "ReorderableList" to include two new right click menus while attempting to maintain the original list. I might have missed a few things visually but it's good enough for my needs. This should allow you to copy this code and paste it, modify the targeting component (along with the custom right click menus) and you should be good to go.