/// <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));
            }
        }
Example #2
0
        /// <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);
                }
            }
        }
Example #3
0
        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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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();
                }
            }
        }
Example #6
0
        /// <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);
        }