/// <summary> /// Handle caching of recursive editors and returns a previously /// created editor of given target if it exists. This prevents /// escalating creations of CustomTargetTool instances (and more). /// </summary> /// <param name="target">Target object.</param> /// <returns>Cached editor or newly created one if it exists. Otherwise null.</returns> public static Editor TryGetOrCreateRecursiveEditor(Object target) { if (target == null) { return(null); } Editor editor = null; if (m_recursiveEditors.TryGetValue(target, out editor)) { // Old editor with destroyed target, e.g., when entering // edit coming from play mode. if (editor.target == null) { ReleaseRecursiveEditor(target); } else { return(editor); } } editor = InspectorEditor.CreateRecursive(target); if (editor != null) { m_recursiveEditors.Add(target, editor); } return(editor); }
public static object GenericListDrawer(object obj, InvokeWrapper wrapper, GUISkin skin) { System.Collections.IList list = wrapper.Get <System.Collections.IList>(obj); var target = obj as Object; if (GUI.Foldout(EditorData.Instance.GetData(target, wrapper.Member.Name), MakeLabel(wrapper.Member), skin)) { object insertElementBefore = null; object insertElementAfter = null; object eraseElement = null; var buttonLayout = new GUILayoutOption[] { GUILayout.Width(26), GUILayout.Height(18) }; using (new GUI.Indent(12)) { foreach (var listObject in list) { GUI.Separator(); using (new GUI.Indent(12)) { GUILayout.BeginHorizontal(); { GUILayout.BeginVertical(); { // Using target to render listObject since it normally (CollisionGroupEntry) isn't an Object. InspectorEditor.DrawMembersGUI(new Object[] { target }, ignored => listObject); } GUILayout.EndVertical(); using (GUI.NodeListButtonColor) { if (GUILayout.Button(GUI.MakeLabel(GUI.Symbols.ListInsertElementBefore.ToString(), false, "Insert new element before this"), skin.button, buttonLayout)) { insertElementBefore = listObject; } if (GUILayout.Button(GUI.MakeLabel(GUI.Symbols.ListInsertElementAfter.ToString(), false, "Insert new element after this"), skin.button, buttonLayout)) { insertElementAfter = listObject; } if (GUILayout.Button(GUI.MakeLabel(GUI.Symbols.ListEraseElement.ToString(), false, "Erase this element"), skin.button, buttonLayout)) { eraseElement = listObject; } } } GUILayout.EndHorizontal(); } } if (list.Count == 0) { GUILayout.Label(GUI.MakeLabel("Empty", true), skin.label); } else { GUI.Separator(); } } bool addElementToList = false; GUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); using (GUI.NodeListButtonColor) addElementToList = GUILayout.Button(GUI.MakeLabel(GUI.Symbols.ListInsertElementAfter.ToString(), false, "Add new element to list"), skin.button, buttonLayout); } GUILayout.EndHorizontal(); object newObject = null; if (addElementToList || insertElementBefore != null || insertElementAfter != null) { newObject = Activator.CreateInstance(list.GetType().GetGenericArguments()[0], new object[] { }); } if (eraseElement != null) { list.Remove(eraseElement); } else if (newObject != null) { if (addElementToList || (list.Count > 0 && insertElementAfter != null && insertElementAfter == list[list.Count - 1])) { list.Add(newObject); } else if (insertElementAfter != null) { list.Insert(list.IndexOf(insertElementAfter) + 1, newObject); } else if (insertElementBefore != null) { list.Insert(list.IndexOf(insertElementBefore), newObject); } } if (eraseElement != null || newObject != null) { EditorUtility.SetDirty(target); } } // A bit of a hack until I figure out how to handle multi-selection // of lists, if that should be possible at all. We're handling the // list from inside this drawer and by returning null the return // value isn't propagated to any targets. return(null); }
public static void DrawUrdfElement(AGXUnity.IO.URDF.Element element, int elementArrayIndex = -1) { if (element == null) { return; } var dropDownName = string.IsNullOrEmpty(element.Name) ? elementArrayIndex >= 0 ? $"{element.GetType().Name}[{elementArrayIndex}]" : element.GetType().Name : element.Name; if (!InspectorGUI.Foldout(GetEditorData(element, dropDownName), GUI.MakeLabel(InspectorGUISkin.Instance.TagTypename($"Urdf.{element.GetType().Name}") + ' ' + dropDownName))) { return; } using (InspectorGUI.IndentScope.Single) { var ignoreName = element is AGXUnity.IO.URDF.Inertial; if (!ignoreName) { var nameRect = EditorGUILayout.GetControlRect(); EditorGUI.PrefixLabel(nameRect, GUI.MakeLabel("Name"), InspectorEditor.Skin.Label); var orgXMax = nameRect.xMax; nameRect.x += EditorGUIUtility.labelWidth - 14.0f * InspectorGUI.IndentScope.Level; nameRect.xMax = orgXMax; EditorGUI.SelectableLabel(nameRect, element.Name, InspectorEditor.Skin.TextField); } if (element is AGXUnity.IO.URDF.Pose) { DrawUrdfPose(element as AGXUnity.IO.URDF.Pose); } else if (element is AGXUnity.IO.URDF.Material) { DrawUrdfMaterial(element as AGXUnity.IO.URDF.Material); return; } var properties = GetOrFindProperties(element.GetType()); var elementArg = new object[] { element }; var geometry = element as AGXUnity.IO.URDF.Geometry; foreach (var property in properties) { // Ignoring Unity specific properties such as "name" and "hideFlags". if (!char.IsUpper(property.Member.Name[0])) { continue; } if (!InspectorEditor.ShouldBeShownInInspector(property.Member)) { continue; } var containingType = property.GetContainingType(); if (containingType.IsArray) { if (typeof(AGXUnity.IO.URDF.Element).IsAssignableFrom(containingType.GetElementType())) { var array = property.Get <System.Collections.ICollection>(element); if (!InspectorGUI.Foldout(GetEditorData(element, property.Member.Name), InspectorGUI.MakeLabel(property.Member, $" [{array.Count}]"))) { continue; } using (InspectorGUI.IndentScope.Single) { var arrayIndex = 0; foreach (var arrayItem in array) { DrawUrdfElement(arrayItem as AGXUnity.IO.URDF.Element, arrayIndex++); } } } } else if (typeof(AGXUnity.IO.URDF.Element).IsAssignableFrom(containingType)) { DrawUrdfElement(property.Get <AGXUnity.IO.URDF.Element>(element), -1); } else if (geometry == null || IsValidGeometryProperty(geometry, property)) { var drawerMethod = GetDrawerMethod(containingType); drawerMethod.Drawer?.Invoke(null, new object[] { elementArg, property }); } } } }
public static object GenericListDrawer(object[] objects, InvokeWrapper wrapper) { var list = wrapper.Get <System.Collections.IList>(objects[0]); var target = objects[0] as Object; if (InspectorGUI.Foldout(EditorData.Instance.GetData(target, wrapper.Member.Name), InspectorGUI.MakeLabel(wrapper.Member))) { object insertElementBefore = null; object insertElementAfter = null; object eraseElement = null; var skin = InspectorEditor.Skin; var buttonLayout = new GUILayoutOption[] { GUILayout.Width(1.0f * EditorGUIUtility.singleLineHeight), GUILayout.Height(1.0f * EditorGUIUtility.singleLineHeight) }; foreach (var listObject in list) { using (InspectorGUI.IndentScope.Single) { GUILayout.BeginHorizontal(); { InspectorGUI.Separator(1.0f, EditorGUIUtility.singleLineHeight); if (InspectorGUI.Button(MiscIcon.EntryInsertBefore, true, "Insert new element before this.", buttonLayout)) { insertElementBefore = listObject; } if (InspectorGUI.Button(MiscIcon.EntryInsertAfter, true, "Insert new element after this.", buttonLayout)) { insertElementAfter = listObject; } if (InspectorGUI.Button(MiscIcon.EntryRemove, true, "Remove this element.", buttonLayout)) { eraseElement = listObject; } } GUILayout.EndHorizontal(); InspectorEditor.DrawMembersGUI(new Object[] { target }, ignored => listObject); } } InspectorGUI.Separator(1.0f, 0.5f * EditorGUIUtility.singleLineHeight); if (list.Count == 0) { GUILayout.Label(GUI.MakeLabel("Empty", true), skin.Label); } bool addElementToList = false; GUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); addElementToList = InspectorGUI.Button(MiscIcon.EntryInsertAfter, true, "Add new element.", buttonLayout); } GUILayout.EndHorizontal(); object newObject = null; if (addElementToList || insertElementBefore != null || insertElementAfter != null) { newObject = Activator.CreateInstance(list.GetType().GetGenericArguments()[0], new object[] { }); } if (eraseElement != null) { list.Remove(eraseElement); } else if (newObject != null) { if (addElementToList || (list.Count > 0 && insertElementAfter != null && insertElementAfter == list[list.Count - 1])) { list.Add(newObject); } else if (insertElementAfter != null) { list.Insert(list.IndexOf(insertElementAfter) + 1, newObject); } else if (insertElementBefore != null) { list.Insert(list.IndexOf(insertElementBefore), newObject); } } if (eraseElement != null || newObject != null) { EditorUtility.SetDirty(target); } } // A bit of a hack until I figure out how to handle multi-selection // of lists, if that should be possible at all. We're handling the // list from inside this drawer and by returning null the return // value isn't propagated to any targets. return(null); }