示例#1
0
        /// <summary>
        /// Searches for a field with the specified path.
        /// </summary>
        /// <param name="path">
        /// Path to search for. Path entries are readable field names separated with "/". Fields within categories are
        /// placed within a special category group, surrounded by "[]". Some examples:
        /// - myField
        /// - myObject/myField
        /// - myObject/[myCategory]/myField
        /// </param>
        /// <param name="fields">List of fields to search. Children will be searched recursively.</param>
        /// <returns>Matching field if one is found, null otherwise.</returns>
        public static InspectableField FindPath(string path, IEnumerable <InspectableField> fields)
        {
            string subPath = GetSubPath(path);

            foreach (var field in fields)
            {
                InspectableField foundField = null;

                if (field.path == subPath)
                {
                    foundField = field;
                }
                else
                {
                    foundField = field.FindPath(subPath);
                }

                if (foundField != null)
                {
                    return(foundField);
                }
            }

            return(null);
        }
示例#2
0
        /// <summary>
        /// Creates a new field accessing the provided property.
        /// </summary>
        /// <param name="title">Title to display on the field.</param>
        /// <param name="name">Name of the field.</param>
        /// <param name="property">Property used to access the field contents.</param>
        /// <param name="style">Optional style used to customize the look of the field.</param>
        /// <param name="fieldCreateCallback">
        /// Optional callback allowing the caller to override how is the field created.
        /// </param>
        private void AddFieldInternal(string title, string name, SerializableProperty property,
                                      InspectableFieldStyleInfo style,
                                      Func <string, InspectableFieldLayout, int, int, InspectableField> fieldCreateCallback)
        {
            int        currentIndex;
            int        childDepth;
            GUILayoutY parentLayout;

            if (category != null)
            {
                currentIndex = categoryIndex;
                parentLayout = category.ChildLayout;
                childDepth   = depth + 1;
            }
            else
            {
                currentIndex = rootIndex;
                parentLayout = layout;
                childDepth   = depth;
            }

            string childPath = string.IsNullOrEmpty(path) ? name : $"{path}/{name}";

            InspectableField inspectableField = null;

            if (fieldCreateCallback != null)
            {
                inspectableField = fieldCreateCallback(path, new InspectableFieldLayout(parentLayout), currentIndex, depth);
            }

            if (inspectableField == null)
            {
                inspectableField = InspectableField.CreateField(context, title, childPath,
                                                                currentIndex, childDepth, new InspectableFieldLayout(parentLayout), property, style);
            }

            if (category != null)
            {
                category.AddChild(inspectableField);
            }
            else
            {
                Fields.Add(inspectableField);
            }

            currentIndex += inspectableField.GetNumLayoutElements();

            if (category != null)
            {
                categoryIndex = currentIndex;
            }
            else
            {
                rootIndex = currentIndex;
            }
        }
            /// <inheritdoc/>
            protected override void CreateValueGUI(GUILayoutY layout)
            {
                InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent;
                SerializableProperty     property   = GetValue <SerializableProperty>();

                string entryPath = dictParent.Path + "Value[" + RowIdx + "]";

                FieldValue = CreateField(dictParent.Context, "Value", entryPath, 0, Depth + 1,
                                         new InspectableFieldLayout(layout), property);
            }
            /// <inheritdoc/>
            protected override GUILayoutX CreateGUI(GUILayoutY layout)
            {
                InspectableListGUI   listParent = (InspectableListGUI)parent;
                SerializableProperty property   = GetValue <SerializableProperty>();

                string entryPath = listParent.Path + "[" + SeqIndex + "]";

                Field = CreateField(listParent.Context, SeqIndex + ".", entryPath, 0, Depth + 1,
                                    new InspectableFieldLayout(layout), property, new InspectableFieldStyleInfo());

                return(Field.GetTitleLayout());
            }
        /// <inheritdoc />
        internal override void FocusOnField(string path)
        {
            if (path.StartsWith("materialParams"))
            {
                string   subPath      = InspectableField.GetSubPath(path, 2);
                string[] subPathParts = subPath.Split('/');

                if (subPathParts.Length < 2)
                {
                    return;
                }

                int lastLeftIdx  = subPathParts[0].LastIndexOf('[');
                int lastRightIdx = subPathParts[0].LastIndexOf(']', lastLeftIdx);

                if (lastLeftIdx == -1 || lastRightIdx == -1)
                {
                    return;
                }

                int count = lastRightIdx - 1 - lastLeftIdx;
                if (count <= 0)
                {
                    return;
                }

                string arrayIdxStr = subPath.Substring(lastLeftIdx, count);

                if (!int.TryParse(arrayIdxStr, out int idx))
                {
                    return;
                }

                if (idx >= materialParams.Count)
                {
                    return;
                }

                MaterialParamGUI[] entries = materialParams[idx];
                foreach (var entry in entries)
                {
                    string fieldSubPath = subPathParts.Length > 2 ? subPathParts[2] : null;

                    if (entry.Param.name == subPathParts[1])
                    {
                        entry.SetHasFocus(fieldSubPath);
                        break;
                    }
                }
            }

            base.FocusOnField(path);
        }
        /// <summary>
        /// Creates new generic inspector field drawer for the specified object.
        /// </summary>
        /// <param name="obj">Object whose fields to create the GUI for.</param>
        /// <param name="parent">Parent Inspector to draw in.</param>
        /// <param name="layout">Parent layout that all the field GUI elements will be added to.</param>
        /// <param name="overrideCallback">
        /// Optional callback that allows you to override the look of individual fields in the object. If non-null the
        /// callback will be called with information about every field in the provided object. If the callback returns
        /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type
        /// will be used.
        /// </param>
        public GenericInspectorDrawer(object obj, Inspector parent, GUILayoutY layout,
                                      InspectableField.FieldOverrideCallback overrideCallback = null)
        {
            if (obj == null)
            {
                return;
            }

            SerializableObject serializableObject = new SerializableObject(obj.GetType(), obj);

            Fields = InspectableField.CreateFields(serializableObject, parent, "", 0, layout, overrideCallback);
        }
示例#7
0
            /// <inheritdoc/>
            protected override GUILayoutX CreateKeyGUI(GUILayoutY layout)
            {
                keyLayout = layout;
                InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent;
                SerializableProperty     property   = GetKey <SerializableProperty>();

                string entryPath = dictParent.Path + "Key[" + RowIdx + "]";

                fieldKey = CreateField(dictParent.Inspector, "Key", entryPath, 0, Depth + 1,
                                       new InspectableFieldLayout(layout), property);

                return(fieldKey.GetTitleLayout());
            }
        /// <inheritdoc />
        public override InspectableField FindPath(string path)
        {
            string subPath = GetSubPath(path, depth + 1);

            if (string.IsNullOrEmpty(subPath))
            {
                return(null);
            }

            int lastLeftIdx  = subPath.LastIndexOf('[');
            int lastRightIdx = subPath.LastIndexOf(']', lastLeftIdx);

            if (lastLeftIdx == -1 || lastRightIdx == -1)
            {
                return(null);
            }

            int count = lastRightIdx - 1 - lastLeftIdx;

            if (count <= 0)
            {
                return(null);
            }

            string arrayIdxStr = subPath.Substring(lastLeftIdx, count);

            if (!int.TryParse(arrayIdxStr, out int idx))
            {
                return(null);
            }

            if (idx >= listGUIField.NumRows)
            {
                return(null);
            }

            InspectableListGUIRow row   = listGUIField.GetRow(idx);
            InspectableField      field = row?.Field;

            if (field != null)
            {
                if (field.Path == path)
                {
                    return(field);
                }

                return(field.FindPath(path));
            }

            return(null);
        }
示例#9
0
            /// <inheritdoc/>
            protected override GUILayoutX CreateGUI(GUILayoutY layout)
            {
                InspectableArrayGUI  arrayParent = (InspectableArrayGUI)parent;
                SerializableProperty property    = GetValue <SerializableProperty>();

                InspectableFieldStyleInfo styleInfo = arrayParent.Style.Clone();

                styleInfo.StyleFlags &= ~InspectableFieldStyleFlags.NativeWrapper;

                string entryPath = arrayParent.Path + "[" + SeqIndex + "]";

                Field = CreateField(arrayParent.Context, SeqIndex + ".", entryPath, 0, Depth + 1,
                                    new InspectableFieldLayout(layout), property, styleInfo);

                return(Field.GetTitleLayout());
            }
示例#10
0
 /// <summary>
 /// Registers a new child field in the category.
 /// </summary>
 /// <param name="child">Child field to add to the category. The field must have the category
 /// <see cref="ChildLayout"/> as its parent.</param>
 public void AddChild(InspectableField child)
 {
     children.Add(child);
 }
示例#11
0
        /// <summary>
        /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type
        /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations
        /// (like ones for primitives like int or bool), or a user defined implementation defined with a
        /// <see cref="CustomInspector"/> attribute.
        /// </summary>
        /// <param name="context">Context shared by all inspectable fields created by the same parent.</param>
        /// <param name="title">Name of the property, or some other value to set as the title.</param>
        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</param>
        /// <param name="layoutIndex">Index into the parent layout at which to insert the GUI elements for the field .</param>
        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
        ///                     contain other fields, in which case you should increase this value by one.</param>
        /// <param name="layout">Parent layout that all the field elements will be added to.</param>
        /// <param name="property">Serializable property referencing the array whose contents to display.</param>
        /// <param name="style">Information that can be used for customizing field rendering and behaviour.</param>
        /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property
        ///          of the provided type.</returns>
        public static InspectableField CreateField(InspectableContext context, string title, string path, int layoutIndex,
                                                   int depth, InspectableFieldLayout layout, SerializableProperty property, InspectableFieldStyleInfo style = null)
        {
            InspectableField field = null;

            Type type = property.InternalType;

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(RRef <>))
            {
                type = type.GenericTypeArguments[0];
            }

            Type customInspectable = InspectorUtility.GetCustomInspectable(type);

            if (customInspectable != null)
            {
                field = (InspectableField)Activator.CreateInstance(customInspectable, context, title, path, depth, layout,
                                                                   property, style);
            }
            else
            {
                switch (property.Type)
                {
                case SerializableProperty.FieldType.Int:
                    if (style != null && style.StyleFlags.HasFlag(InspectableFieldStyleFlags.AsLayerMask))
                    {
                        field = new InspectableLayerMask(context, title, path, depth, layout, property);
                    }
                    else
                    {
                        if (style?.RangeStyle == null || !style.RangeStyle.Slider)
                        {
                            field = new InspectableInt(context, title, path, depth, layout, property, style);
                        }
                        else
                        {
                            field = new InspectableRangedInt(context, title, path, depth, layout, property, style);
                        }
                    }

                    break;

                case SerializableProperty.FieldType.Float:
                    if (style?.RangeStyle == null || !style.RangeStyle.Slider)
                    {
                        field = new InspectableFloat(context, title, path, depth, layout, property, style);
                    }
                    else
                    {
                        field = new InspectableRangedFloat(context, title, path, depth, layout, property, style);
                    }
                    break;

                case SerializableProperty.FieldType.Bool:
                    field = new InspectableBool(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Color:
                    field = new InspectableColor(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.ColorGradient:
                    field = new InspectableColorGradient(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Curve:
                    field = new InspectableCurve(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.FloatDistribution:
                    field = new InspectableFloatDistribution(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Vector2Distribution:
                    field = new InspectableVector2Distribution(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Vector3Distribution:
                    field = new InspectableVector3Distribution(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.ColorDistribution:
                    field = new InspectableColorDistribution(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.String:
                    field = new InspectableString(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Vector2:
                    field = new InspectableVector2(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Vector3:
                    field = new InspectableVector3(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Vector4:
                    field = new InspectableVector4(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Quaternion:
                    if (style != null && style.StyleFlags.HasFlag(InspectableFieldStyleFlags.AsQuaternion))
                    {
                        field = new InspectableQuaternion(context, title, path, depth, layout, property);
                    }
                    else
                    {
                        field = new InspectableEuler(context, title, path, depth, layout, property);
                    }
                    break;

                case SerializableProperty.FieldType.Resource:
                    field = new InspectableResource(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.RRef:
                    field = new InspectableRRef(context, title, path, depth, layout, property, style);
                    break;

                case SerializableProperty.FieldType.GameObjectRef:
                    field = new InspectableGameObjectRef(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Object:
                    field = new InspectableObject(context, title, path, depth, layout, property, style);
                    break;

                case SerializableProperty.FieldType.Array:
                    field = new InspectableArray(context, title, path, depth, layout, property, style);
                    break;

                case SerializableProperty.FieldType.List:
                    field = new InspectableList(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Dictionary:
                    field = new InspectableDictionary(context, title, path, depth, layout, property);
                    break;

                case SerializableProperty.FieldType.Enum:
                    field = new InspectableEnum(context, title, path, depth, layout, property);
                    break;
                }
            }

            if (field == null)
            {
                throw new Exception("No inspector exists for the provided field type.");
            }

            field.Initialize(layoutIndex);
            field.Refresh(layoutIndex);

            return(field);
        }
示例#12
0
        /// <summary>
        /// Creates inspectable fields all the fields/properties of the specified object.
        /// </summary>
        /// <param name="obj">Object whose fields the GUI will be drawn for.</param>
        /// <param name="context">Context shared by all inspectable fields created by the same parent.</param>
        /// <param name="path">Full path to the field this provided object was retrieved from.</param>
        /// <param name="depth">
        /// Determines how deep within the inspector nesting hierarchy is this objects. Some fields may contain other
        /// fields, in which case you should increase this value by one.
        /// </param>
        /// <param name="layout">Parent layout that all the field GUI elements will be added to.</param>
        /// <param name="overrideCallback">
        /// Optional callback that allows you to override the look of individual fields in the object. If non-null the
        /// callback will be called with information about every field in the provided object. If the callback returns
        /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type
        /// will be used.
        /// </param>
        public static List <InspectableField> CreateFields(SerializableObject obj, InspectableContext context, string path,
                                                           int depth, GUILayoutY layout, FieldOverrideCallback overrideCallback = null)
        {
            // Retrieve fields and sort by order
            SerializableField[] fields = obj.Fields;
            Array.Sort(fields,
                       (x, y) =>
            {
                int orderX = x.Flags.HasFlag(SerializableFieldAttributes.Order) ? x.Style.Order : 0;
                int orderY = y.Flags.HasFlag(SerializableFieldAttributes.Order) ? y.Style.Order : 0;

                return(orderX.CompareTo(orderY));
            });

            // Generate per-field GUI while grouping by category
            int    rootIndex             = 0;
            int    categoryIndex         = 0;
            string categoryName          = null;
            InspectableCategory category = null;

            List <InspectableField> inspectableFields = new List <InspectableField>();

            foreach (var field in fields)
            {
                if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable))
                {
                    continue;
                }

                if (field.Flags.HasFlag(SerializableFieldAttributes.Category))
                {
                    string newCategory = field.Style.CategoryName;
                    if (!string.IsNullOrEmpty(newCategory) && categoryName != newCategory)
                    {
                        string categoryPath = path + "/[" + newCategory + "]";
                        category = new InspectableCategory(context, newCategory, categoryPath, depth,
                                                           new InspectableFieldLayout(layout));

                        category.Initialize(rootIndex);
                        category.Refresh(rootIndex);
                        rootIndex += category.GetNumLayoutElements();

                        inspectableFields.Add(category);

                        categoryName  = newCategory;
                        categoryIndex = 0;
                    }
                    else
                    {
                        categoryName = null;
                        category     = null;
                    }
                }

                int        currentIndex;
                GUILayoutY parentLayout;
                if (category != null)
                {
                    currentIndex = categoryIndex;
                    parentLayout = category.ChildLayout;
                }
                else
                {
                    currentIndex = rootIndex;
                    parentLayout = layout;
                }

                string fieldName = GetReadableIdentifierName(field.Name);
                string childPath = string.IsNullOrEmpty(path) ? fieldName : path + "/" + fieldName;

                InspectableField inspectableField = null;

                if (overrideCallback != null)
                {
                    inspectableField = overrideCallback(field, context, path, new InspectableFieldLayout(parentLayout),
                                                        currentIndex, depth);
                }

                if (inspectableField == null)
                {
                    inspectableField = CreateField(context, fieldName, childPath,
                                                   currentIndex, depth, new InspectableFieldLayout(parentLayout), field.GetProperty(),
                                                   InspectableFieldStyle.Create(field));
                }

                if (category != null)
                {
                    category.AddChild(inspectableField);
                }
                else
                {
                    inspectableFields.Add(inspectableField);
                }

                currentIndex += inspectableField.GetNumLayoutElements();

                if (category != null)
                {
                    categoryIndex = currentIndex;
                }
                else
                {
                    rootIndex = currentIndex;
                }
            }

            return(inspectableFields);
        }
示例#13
0
        /// <inheritdoc />
        public override InspectableField FindPath(string path)
        {
            string subPath = GetSubPath(path, depth + 1);

            if (string.IsNullOrEmpty(subPath))
            {
                return(null);
            }

            int  lastLeftIdx  = subPath.LastIndexOf("Key[");
            int  lastRightIdx = -1;
            bool isKey;

            if (lastLeftIdx != -1)
            {
                lastRightIdx = subPath.LastIndexOf(']', lastLeftIdx);
                isKey        = true;
            }
            else
            {
                lastLeftIdx  = subPath.LastIndexOf("Value[");
                lastRightIdx = subPath.LastIndexOf(']', lastLeftIdx);

                isKey = false;
            }

            if (lastLeftIdx == -1 || lastRightIdx == -1)
            {
                return(null);
            }

            int count = lastRightIdx - 1 - lastLeftIdx;

            if (count <= 0)
            {
                return(null);
            }

            string arrayIdxStr = subPath.Substring(lastLeftIdx, count);

            if (!int.TryParse(arrayIdxStr, out int idx))
            {
                return(null);
            }
            ;

            if (idx >= dictionaryGUIField.NumRows)
            {
                return(null);
            }

            InspectableDictionaryGUIRow row   = dictionaryGUIField.GetRow(idx);
            InspectableField            field = null;

            if (isKey)
            {
                field = row?.FieldKey;
            }
            else
            {
                field = row?.FieldValue;
            }

            if (field != null)
            {
                if (field.Path == path)
                {
                    return(field);
                }

                return(field.FindPath(path));
            }

            return(null);
        }
示例#14
0
        /// <summary>
        /// Changes keyboard focus to the provided field.
        /// </summary>
        /// <param name="path">Path to the field on the object being inspected.</param>
        public void FocusOnField(string path)
        {
            InspectableField field = InspectableField.FindPath(path, 0, Fields);

            field?.SetHasFocus();
        }
示例#15
0
        /// <summary>
        /// Creates the default inspector GUI for the provided object.
        /// </summary>
        /// <param name="obj">Object whose fields to create the GUI for.</param>
        /// <param name="subType">
        /// If not null, the added fields will be limited to this particular type (not including any base types the actual
        /// object type might be a part of). If null, then fields for the entire class hierarchy of the provided object's
        /// type will be created.
        /// </param>
        /// <param name="overrideCallback">
        /// Optional callback that allows you to override the look of individual fields in the object. If non-null the
        /// callback will be called with information about every field in the provided object. If the callback returns
        /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type
        /// will be used.
        /// </param>
        public void AddDefault(SerializableObject obj, Type subType = null, FieldOverrideCallback overrideCallback = null)
        {
            if (obj == null)
            {
                return;
            }

            // Retrieve fields and sort by order
            List <SerializableField> fields = new List <SerializableField>();

            while (obj != null)
            {
                if (subType == null || subType == obj.Type)
                {
                    SerializableField[] subTypeFields = obj.Fields;
                    Array.Sort(subTypeFields,
                               (x, y) =>
                    {
                        int orderX = x.Flags.HasFlag(SerializableFieldAttributes.Order) ? x.Style.Order : 0;
                        int orderY = y.Flags.HasFlag(SerializableFieldAttributes.Order) ? y.Style.Order : 0;

                        return(orderX.CompareTo(orderY));
                    });

                    fields.AddRange(subTypeFields);
                }

                obj = obj.Base;
            }

            // Generate per-field GUI while grouping by category
            foreach (var field in fields)
            {
                if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable))
                {
                    continue;
                }

                if (field.Flags.HasFlag(SerializableFieldAttributes.Category))
                {
                    string newCategory = field.Style.CategoryName;
                    if (!string.IsNullOrEmpty(newCategory) && categoryName != newCategory)
                    {
                        BeginCategory(newCategory);
                    }
                    else
                    {
                        EndCategory();
                    }
                }

                string fieldName    = field.Name;
                string readableName = InspectableField.GetReadableIdentifierName(fieldName);

                Func <string, InspectableFieldLayout, int, int, InspectableField> callback = null;
                if (overrideCallback != null)
                {
                    callback = (path, fieldLayout, layoutIndex, depth) =>
                               overrideCallback(field, context, path, fieldLayout, layoutIndex, depth);
                }

                AddFieldInternal(readableName, fieldName, field.GetProperty(), InspectableFieldStyle.Create(field),
                                 callback);
            }
        }
        /// <summary>
        /// Creates inspectable fields all the fields/properties of the specified object.
        /// </summary>
        /// <param name="obj">Object whose fields the GUI will be drawn for.</param>
        /// <param name="parent">Parent Inspector to draw in.</param>
        /// <param name="path">Full path to the field this provided object was retrieved from.</param>
        /// <param name="depth">
        /// Determines how deep within the inspector nesting hierarchy is this objects. Some fields may contain other
        /// fields, in which case you should increase this value by one.
        /// </param>
        /// <param name="layout">Parent layout that all the field GUI elements will be added to.</param>
        /// <param name="overrideCallback">
        /// Optional callback that allows you to override the look of individual fields in the object. If non-null the
        /// callback will be called with information about every field in the provided object. If the callback returns
        /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type
        /// will be used.
        /// </param>
        public static List <InspectableField> CreateFields(SerializableObject obj, Inspector parent, string path,
                                                           int depth, GUILayoutY layout, FieldOverrideCallback overrideCallback = null)
        {
            // Retrieve fields and sort by order
            SerializableField[] fields = obj.Fields;
            Array.Sort(fields,
                       (x, y) =>
            {
                int orderX = x.Flags.HasFlag(SerializableFieldAttributes.Order) ? x.Style.Order : 0;
                int orderY = y.Flags.HasFlag(SerializableFieldAttributes.Order) ? y.Style.Order : 0;

                return(orderX.CompareTo(orderY));
            });

            // Generate per-field GUI while grouping by category
            Dictionary <string, Tuple <int, GUILayoutY> > categories = new Dictionary <string, Tuple <int, GUILayoutY> >();

            int rootIndex = 0;
            List <InspectableField> inspectableFields = new List <InspectableField>();

            foreach (var field in fields)
            {
                if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable))
                {
                    continue;
                }

                string category = null;
                if (field.Flags.HasFlag(SerializableFieldAttributes.Category))
                {
                    category = field.Style.CategoryName;
                }

                Tuple <int, GUILayoutY> categoryInfo = null;
                if (!string.IsNullOrEmpty(category))
                {
                    if (!categories.TryGetValue(category, out categoryInfo))
                    {
                        InspectableFieldLayout fieldLayout        = new InspectableFieldLayout(layout);
                        GUILayoutY             categoryRootLayout = fieldLayout.AddLayoutY(rootIndex);
                        GUILayoutX             guiTitleLayout     = categoryRootLayout.AddLayoutX();

                        bool isExpanded = parent.Persistent.GetBool(path + "/[" + category + "]_Expanded");

                        GUIToggle guiFoldout = new GUIToggle(category, EditorStyles.Foldout);
                        guiFoldout.Value           = isExpanded;
                        guiFoldout.AcceptsKeyFocus = false;
                        guiFoldout.OnToggled      += x =>
                        {
                            parent.Persistent.SetBool(path + "/[" + category + "]_Expanded", x);
                        };
                        guiTitleLayout.AddElement(guiFoldout);

                        GUILayoutX categoryContentLayout = categoryRootLayout.AddLayoutX();
                        categoryContentLayout.AddSpace(IndentAmount);

                        GUIPanel   guiContentPanel  = categoryContentLayout.AddPanel();
                        GUILayoutX guiIndentLayoutX = guiContentPanel.AddLayoutX();
                        guiIndentLayoutX.AddSpace(IndentAmount);
                        GUILayoutY guiIndentLayoutY = guiIndentLayoutX.AddLayoutY();
                        guiIndentLayoutY.AddSpace(IndentAmount);
                        GUILayoutY categoryLayout = guiIndentLayoutY.AddLayoutY();
                        guiIndentLayoutY.AddSpace(IndentAmount);
                        guiIndentLayoutX.AddSpace(IndentAmount);
                        categoryContentLayout.AddSpace(IndentAmount);

                        short  backgroundDepth = (short)(Inspector.START_BACKGROUND_DEPTH - depth - 1);
                        string bgPanelStyle    = depth % 2 == 0
                            ? EditorStylesInternal.InspectorContentBgAlternate
                            : EditorStylesInternal.InspectorContentBg;
                        GUIPanel   backgroundPanel    = guiContentPanel.AddPanel(backgroundDepth);
                        GUITexture inspectorContentBg = new GUITexture(null, bgPanelStyle);
                        backgroundPanel.AddElement(inspectorContentBg);

                        categories[category] = new Tuple <int, GUILayoutY>(0, categoryLayout);
                        rootIndex++;
                    }
                }

                int        currentIndex;
                GUILayoutY parentLayout;
                if (categoryInfo != null)
                {
                    currentIndex = categoryInfo.Item1;
                    parentLayout = categoryInfo.Item2;
                }
                else
                {
                    currentIndex = rootIndex;
                    parentLayout = layout;
                }

                string fieldName = field.Name;
                string childPath = string.IsNullOrEmpty(path) ? fieldName : path + "/" + fieldName;

                InspectableField inspectableField = null;

                if (overrideCallback != null)
                {
                    inspectableField = overrideCallback(field, parent, path, new InspectableFieldLayout(parentLayout),
                                                        currentIndex, depth);
                }

                if (inspectableField == null)
                {
                    inspectableField = CreateField(parent, fieldName, childPath,
                                                   currentIndex, depth, new InspectableFieldLayout(parentLayout), field.GetProperty(),
                                                   InspectableFieldStyle.Create(field));
                }

                inspectableFields.Add(inspectableField);
                currentIndex += inspectableField.GetNumLayoutElements();

                if (categoryInfo != null)
                {
                    categories[category] = new Tuple <int, GUILayoutY>(currentIndex, parentLayout);
                }
                else
                {
                    rootIndex = currentIndex;
                }
            }

            return(inspectableFields);
        }