/// <summary> /// Gets all drawers for a given property. /// </summary> public static OdinDrawer[] GetDrawersForProperty(InspectorProperty property) { if (property == null) { throw new ArgumentNullException("property"); } MemberInfo memberInfo = property.Info.MemberInfo; if (property.Info.PropertyType == PropertyType.Method) { return(GetDrawersForInstanceMethod(property.Info.MemberInfo)); } else if (property.Info.PropertyType == PropertyType.Group) { PropertyGroupAttribute attr = property.Info.GetAttribute <PropertyGroupAttribute>(); if (attr != null) { Type attrType = attr.GetType(); return(GetPropertyGroupDrawers(attrType)); } else { throw new InvalidOperationException("Property group property had no property group attribute!"); } } else { bool isListElement = property.ParentValueProperty != null && property.ParentValueProperty.Info == property.Info; return(DrawerLocator.GetDrawersForMemberInfo(memberInfo, property.ValueEntry.TypeOfValue, isListElement)); } }
/// <summary> /// Populates a generic menu with items from all drawers for this property that implement <see cref="IDefinesGenericMenuItems"/>. /// </summary> public void PopulateGenericMenu(GenericMenu genericMenu) { if (genericMenu == null) { throw new ArgumentNullException("genericMenu"); } OdinDrawer[] drawers = DrawerLocator.GetDrawersForProperty(this); for (int i = 0; i < drawers.Length; i++) { var drawer = drawers[i] as IDefinesGenericMenuItems; if (drawer != null) { drawer.PopulateGenericMenu(this, genericMenu); } } }
private static bool CanShowInvalidAttributeErrorForMember(MemberInfo memberInfo, Type containedValueType, Type attrType) { if (memberInfo is MethodInfo) { return(false); } var memberValueType = memberInfo.GetReturnType(); if (containedValueType == typeof(NoType) || memberValueType == typeof(object) || memberInfo.IsDefined <SuppressInvalidAttributeErrorAttribute>()) { return(false); } var drawers = DrawerLocator.GetAttributeDrawers(attrType, containedValueType); if (drawers.Length > 0) { return(false); } if (memberValueType.ImplementsOpenGenericInterface(typeof(IList <>))) { Type listElementType = memberValueType.GetArgumentsOfInheritedOpenGenericInterface(typeof(IList <>))[0]; if (listElementType == typeof(object)) { return(false); } drawers = DrawerLocator.GetAttributeDrawers(attrType, listElementType); if (drawers.Length > 0) { return(false); } } return(true); }
/// <summary> /// <para>Calls the next drawer in the drawer chain.</para> /// <para> /// ODIN supports multiple drawers being used to draw any given property. This method calls /// the next drawer in the drawer chain provided by the <see cref="DrawerLocator" />. /// The order of the drawer chain is defined using the <see cref="DrawerPriorityAttribute" />. /// </para> /// </summary> /// <param name="property">The property.</param> /// <param name="label">The label. Null is allowed if you wish no label to be drawn.</param> /// <returns>Returns true, if a next drawer was called, otherwise a warning message is shown in the inspector and false is returned.</returns> protected bool CallNextDrawer(InspectorProperty property, GUIContent label) { var nextDrawer = DrawerLocator.GetNextDrawer(this, property); if (nextDrawer != null) { property.IncrementDrawerChainIndex(); nextDrawer.DrawProperty(property, label); return(true); } else if (property.ValueEntry != null) { GUILayout.BeginHorizontal(); { if (label != null) { EditorGUILayout.PrefixLabel(label); } AllEditorGUI.WarningMessageBox("There is no custom drawer defined for type '" + property.ValueEntry.TypeOfValue.GetNiceName() + "', and the type has no members to draw."); } GUILayout.EndHorizontal(); } else { GUILayout.BeginHorizontal(); { if (label != null) { EditorGUILayout.PrefixLabel(label); } AllEditorGUI.WarningMessageBox("There is no drawer defined for property " + property.NiceName + " of type " + property.Info.PropertyType + "."); } GUILayout.EndHorizontal(); } return(false); }
/// <summary> /// Draws a property in the inspector using a given label. /// </summary> public static void DrawProperty(InspectorProperty property, GUIContent label) { if (property == null) { throw new ArgumentNullException("property"); } bool popGUIEnabled = false; try { property.PushDraw(); if ((property.RecursiveDrawDepth + InlineEditorAttributeDrawer.CurrentInlineEditorDrawDepth) > GeneralDrawerConfig.Instance.MaxRecursiveDrawDepth) { AllEditorGUI.ErrorMessageBox("The property '" + property.NiceName + "' has exceeded the maximum recursive draw depth limit of " + GeneralDrawerConfig.Instance.MaxRecursiveDrawDepth + "."); return; } if (property.ValueEntry != null && !property.SupportsPrefabModifications && property.Tree.HasPrefabs && !GUIHelper.IsDrawingDictionaryKey && (property.Info.PropertyType == PropertyType.ReferenceType || property.Info.PropertyType == PropertyType.ValueType)) { if ((property.Parent == null || property.Parent.SupportsPrefabModifications) && GeneralDrawerConfig.Instance.ShowPrefabModificationsDisabledMessage) { AllEditorGUI.InfoMessageBox("The property '" + property.NiceName + "' does not support being modified on prefab instances. (You can disable this message the general drawer config.)"); } GUIHelper.PushGUIEnabled(false); popGUIEnabled = true; } OdinDrawer[] drawers = DrawerLocator.GetDrawersForProperty(property); GUIHelper.BeginLayoutMeasuring(); { try { if (drawers.Length > 0) { bool setIsBoldState = property.ValueEntry != null && Event.current.type == EventType.Repaint; if (setIsBoldState) { var boldState = property.ValueEntry.ValueChangedFromPrefab; if (GUIHelper.IsDrawingDictionaryKey) { // Always propagate changed state down through dictionary keys boldState |= GUIHelper.IsBoldLabel; } GUIHelper.PushIsBoldLabel(boldState); } drawers[0].DrawProperty(property, label); if (setIsBoldState) { GUIHelper.PopIsBoldLabel(); } } else { if (property.Info.PropertyType == PropertyType.Method) { EditorGUILayout.LabelField(property.NiceName, "No drawers could be found for the method property '" + property.Name + "' with signature '" + property.Info.MemberInfo.GetNiceName() + "'."); } else if (property.Info.PropertyType == PropertyType.Group) { var attr = property.Info.GetAttribute <PropertyGroupAttribute>(); if (attr != null) { EditorGUILayout.LabelField(property.NiceName, "No drawers could be found for the property group '" + property.Name + "' with property group attribute type '" + attr.GetType().GetNiceName() + "'."); } else { EditorGUILayout.LabelField(property.NiceName, "No drawers could be found for the property group '" + property.Name + "'."); } } //else if (property.Info.GetAttribute<HideInInspector>() == null) //{ // EditorGUILayout.LabelField(property.NiceName, "No drawers could be found for the value property '" + property.Name + "' of type '" + property.ValueEntry.TypeOfValue.GetNiceName() + "'."); //} } } catch (Exception ex) { if (ex is ExitGUIException || ex.InnerException is ExitGUIException) { throw ex; } else { var msg = "This error occurred while being drawn by ODIN. \n" + "ODIN Property Path: " + property.Path + "\n" + "ODIN Drawer Chain: " + string.Join(", ", drawers.Select(n => n.GetType().GetNiceName()).ToArray()) + "."; Debug.LogException(new OdinPropertyException(msg, ex)); } } } if (Event.current.type != EventType.Layout) { property.LastDrawnValueRect = GUIHelper.EndLayoutMeasuring(); } } finally { property.PopDraw(); if (popGUIEnabled) { GUIHelper.PopGUIEnabled(); } } }
/// <summary> /// Get all primary value drawers for a given member info with a given contained value type. /// </summary> public static OdinDrawer[] GetDrawersForMemberInfo(MemberInfo memberInfo, Type containedValueType = null, bool isListElement = false) { if (containedValueType == null) { containedValueType = typeof(NoType); } if (memberInfo == null) { // This happens for Unity aliased properties memberInfo = NoMemberInfo; } OdinDrawer[] result; var cache = isListElement ? DrawerLocator.ListElementDrawerCache : DrawerLocator.MemberDrawerCache; if (!cache.TryGetInnerValue(memberInfo, containedValueType, out result)) { List <OdinDrawer> resultList = new List <OdinDrawer>(); // Find all possible attribute drawers foreach (var attribute in memberInfo.GetAttributes <Attribute>(true)) { var attrType = attribute.GetType(); if (isListElement && (attrType.IsDefined <DontApplyToListElementsAttribute>(true) || attrType.GetType() == typeof(UnityEngine.SpaceAttribute))) { continue; } var attrDrawers = DrawerLocator.GetAttributeDrawers(attrType, containedValueType, true); if (attrDrawers.Length > 0) { resultList.AddRange(attrDrawers); } else if (!isListElement && CanShowInvalidAttributeErrorForMember(memberInfo, containedValueType, attrType)) { List <DrawerInfo> validDrawers = new List <DrawerInfo>(); if (DrawerLocator.AttributeDrawerMap.ContainsKey(attrType)) { validDrawers.AddRange(DrawerLocator.AttributeDrawerMap[attrType]); } if (validDrawers.Count > 0) { // Inject a drawer to state that this drawer-relevant attribute is invalid for the current value type resultList.Add(new InvalidTypeForAttributeDrawer(memberInfo.GetNiceName(), attrType, containedValueType, validDrawers)); } } } if (memberInfo is MethodInfo) { // Add the standard method drawer var drawer = GetDrawer(typeof(MethodPropertyDrawer <>).MakeGenericType(memberInfo.DeclaringType), false); resultList.Add(drawer); } else { // Add the standard value drawers resultList.AddRange(DrawerLocator.GetValueDrawers(containedValueType)); } // Sort all drawers by priority, and then order them by their original added order Dictionary <OdinDrawer, int> originalIndices = new Dictionary <OdinDrawer, int>(resultList.Count); for (int i = 0; i < resultList.Count; i++) { originalIndices[resultList[i]] = i; } result = resultList.OrderByDescending(n => GetDrawerPriority(n.GetType())) .ThenBy(n => originalIndices[n]).ToArray(); cache.AddInner(memberInfo, containedValueType, result); } return(result); }