Browse Source

Feature: Added support for categories when creating inspector GUI through the new drawer interface

BearishSun 6 years ago
parent
commit
df7268c7bd

+ 4 - 0
Source/EditorManaged/Inspectors/D6JointInspector.cs

@@ -23,6 +23,7 @@ namespace bs.Editor
 
 
             drawer.AddDefault(joint, typeof(D6Joint));
             drawer.AddDefault(joint, typeof(D6Joint));
 
 
+            drawer.BeginCategory("Motion constraints");
             for (int i = 0; i < (int)D6JointAxis.Count; i++)
             for (int i = 0; i < (int)D6JointAxis.Count; i++)
             {
             {
                 D6JointAxis axis = (D6JointAxis)i;
                 D6JointAxis axis = (D6JointAxis)i;
@@ -31,6 +32,7 @@ namespace bs.Editor
                 drawer.AddField(entryName, () => joint.GetMotion(axis), x => joint.SetMotion(axis, x));
                 drawer.AddField(entryName, () => joint.GetMotion(axis), x => joint.SetMotion(axis, x));
             }
             }
 
 
+            drawer.BeginCategory("Drive");
             drawer.AddField("Drive position", () => joint.DrivePosition, x => joint.SetDriveTransform(x, joint.DriveRotation));
             drawer.AddField("Drive position", () => joint.DrivePosition, x => joint.SetDriveTransform(x, joint.DriveRotation));
             drawer.AddField("Drive rotation", () => joint.DriveRotation.ToEuler(), x => joint.SetDriveTransform(joint.DrivePosition, Quaternion.FromEuler(x)));
             drawer.AddField("Drive rotation", () => joint.DriveRotation.ToEuler(), x => joint.SetDriveTransform(joint.DrivePosition, Quaternion.FromEuler(x)));
             drawer.AddField("Drive linear velocity", () => joint.DriveLinearVelocity, x => joint.SetDriveVelocity(x, joint.DriveAngularVelocity));
             drawer.AddField("Drive linear velocity", () => joint.DriveLinearVelocity, x => joint.SetDriveVelocity(x, joint.DriveAngularVelocity));
@@ -43,6 +45,8 @@ namespace bs.Editor
 
 
                 drawer.AddField(entryName, () => joint.GetDrive(type), x => joint.SetDrive(type, x));
                 drawer.AddField(entryName, () => joint.GetDrive(type), x => joint.SetDrive(type, x));
             }
             }
+
+            drawer.EndCategory();
         }
         }
     }
     }
 
 

+ 104 - 64
Source/EditorManaged/Windows/Inspector/Inspector.cs

@@ -253,6 +253,11 @@ namespace bs.Editor
         private int rootIndex;
         private int rootIndex;
         private List<ConditionalInfo> conditionals;
         private List<ConditionalInfo> conditionals;
 
 
+        // Category
+        private int categoryIndex;
+        private string categoryName;
+        private InspectableCategory category;
+
         /// <summary>
         /// <summary>
         /// Creates new empty inspector field drawer.
         /// Creates new empty inspector field drawer.
         /// </summary>
         /// </summary>
@@ -338,10 +343,6 @@ namespace bs.Editor
             }
             }
 
 
             // Generate per-field GUI while grouping by category
             // Generate per-field GUI while grouping by category
-            int categoryIndex = 0;
-            string categoryName = null;
-            InspectableCategory category = null;
-
             foreach (var field in fields)
             foreach (var field in fields)
             {
             {
                 if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable))
                 if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable))
@@ -351,71 +352,23 @@ namespace bs.Editor
                 {
                 {
                     string newCategory = field.Style.CategoryName;
                     string newCategory = field.Style.CategoryName;
                     if (!string.IsNullOrEmpty(newCategory) && categoryName != newCategory)
                     if (!string.IsNullOrEmpty(newCategory) && categoryName != newCategory)
-                    {
-                        string categoryPath = string.IsNullOrEmpty(path) ? $"[{newCategory}]" : $"{path}/[{newCategory}]";
-                        category = new InspectableCategory(context, newCategory, categoryPath, depth,
-                            new InspectableFieldLayout(layout));
-
-                        category.Initialize(rootIndex);
-                        category.Refresh(rootIndex);
-                        rootIndex += category.GetNumLayoutElements();
-
-                        Fields.Add(category);
-
-                        categoryName = newCategory;
-                        categoryIndex = 0;
-                    }
+                        BeginCategory(newCategory);
                     else
                     else
-                    {
-                        categoryName = null;
-                        category = null;
-                    }
-                }
-
-                int currentIndex;
-                int childDepth;
-                GUILayoutY parentLayout;
-                if (category != null)
-                {
-                    currentIndex = categoryIndex;
-                    parentLayout = category.ChildLayout;
-                    childDepth = depth + 1;
-                }
-                else
-                {
-                    currentIndex = rootIndex;
-                    parentLayout = layout;
-                    childDepth = depth;
+                        EndCategory();
                 }
                 }
 
 
                 string fieldName = field.Name;
                 string fieldName = field.Name;
                 string readableName = InspectableField.GetReadableIdentifierName(fieldName);
                 string readableName = InspectableField.GetReadableIdentifierName(fieldName);
-                string childPath = string.IsNullOrEmpty(path) ? fieldName : $"{path}/{fieldName}";
-
-                InspectableField inspectableField = null;
 
 
+                Func<string, InspectableFieldLayout, int, int, InspectableField> callback = null;
                 if (overrideCallback != null)
                 if (overrideCallback != null)
-                    inspectableField = overrideCallback(field, context, path, new InspectableFieldLayout(parentLayout),
-                        currentIndex, depth);
-
-                if (inspectableField == null)
                 {
                 {
-                    inspectableField = InspectableField.CreateField(context, readableName, childPath,
-                        currentIndex, childDepth, new InspectableFieldLayout(parentLayout), field.GetProperty(),
-                        InspectableFieldStyle.Create(field));
+                    callback = (path, fieldLayout, layoutIndex, depth) =>
+                        overrideCallback(field, context, path, fieldLayout, layoutIndex, depth);
                 }
                 }
 
 
-                if (category != null)
-                    category.AddChild(inspectableField);
-                else
-                    Fields.Add(inspectableField);
-
-                currentIndex += inspectableField.GetNumLayoutElements();
-
-                if (category != null)
-                    categoryIndex = currentIndex;
-                else
-                    rootIndex = currentIndex;
+                AddFieldInternal(readableName, fieldName, field.GetProperty(), InspectableFieldStyle.Create(field),
+                    callback);
             }
             }
         }
         }
 
 
@@ -428,15 +381,64 @@ namespace bs.Editor
         /// <param name="setter">Method that sets a new value of the field.</param>
         /// <param name="setter">Method that sets a new value of the field.</param>
         public void AddField<T>(string name, Func<T> getter, Action<T> setter)
         public void AddField<T>(string name, Func<T> getter, Action<T> setter)
         {
         {
+            SerializableProperty property = SerializableProperty.Create(getter, setter);
+            AddFieldInternal(name, name, property, null, null);
+        }
+
+        /// <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}";
             string childPath = string.IsNullOrEmpty(path) ? name : $"{path}/{name}";
 
 
-            SerializableProperty property = SerializableProperty.Create(getter, setter);
-            InspectableField inspectableField = InspectableField.CreateField(context, name, childPath,
-                rootIndex, depth, new InspectableFieldLayout(layout), property);
+            InspectableField inspectableField = null;
 
 
-            Fields.Add(inspectableField);
+            if (fieldCreateCallback != null)
+                inspectableField = fieldCreateCallback(path, new InspectableFieldLayout(parentLayout), currentIndex, depth);
 
 
-            rootIndex += inspectableField.GetNumLayoutElements();
+            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;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -452,6 +454,40 @@ namespace bs.Editor
             conditionals.Add(new ConditionalInfo(field, callback));
             conditionals.Add(new ConditionalInfo(field, callback));
         }
         }
 
 
+        /// <summary>
+        /// Opens up a new category. Any new fields will be parented to this category. Category must be closed by calling
+        /// <see cref="EndCategory"/> or by calling this method with a new category.
+        /// </summary>
+        /// <param name="name">Name of the category.</param>
+        public void BeginCategory(string name)
+        {
+            if(category != null)
+                EndCategory();
+
+            string categoryPath = string.IsNullOrEmpty(path) ? $"[{name}]" : $"{path}/[{name}]";
+            category = new InspectableCategory(context, name, categoryPath, depth,
+                new InspectableFieldLayout(layout));
+
+            category.Initialize(rootIndex);
+            category.Refresh(rootIndex);
+            rootIndex += category.GetNumLayoutElements();
+
+            Fields.Add(category);
+
+            categoryName = name;
+            categoryIndex = 0;
+        }
+
+        /// <summary>
+        /// Ends the category started with <see cref="BeginCategory"/>.
+        /// </summary>
+        public void EndCategory()
+        {
+            category = null;
+            categoryName = null;
+            categoryIndex = 0;
+        }
+
         /// <summary>
         /// <summary>
         /// Checks if contents of the inspector fields have been modified, and updates them if needed.
         /// Checks if contents of the inspector fields have been modified, and updates them if needed.
         /// </summary>
         /// </summary>
@@ -495,6 +531,10 @@ namespace bs.Editor
             Fields.Clear();
             Fields.Clear();
             conditionals?.Clear();
             conditionals?.Clear();
             rootIndex = 0;
             rootIndex = 0;
+
+            category = null;
+            categoryName = null;
+            categoryIndex = 0;
         }
         }
 
 
         /// <summary>
         /// <summary>