Browse Source

Prototyping the Inspector

Marko Pintera 11 years ago
parent
commit
2c7f1c7b3d

+ 46 - 16
Inspector.txt

@@ -1,10 +1,5 @@
 Needed controls:
 Needed controls:
-IntField (With drag sliding)
-FloatField (With drag sliding)
-ToggleFIeld
-Vector2 field
-Vector3 field
-Vector4 field
+Toggle field
 Color field
 Color field
 Array field/List field/Dictionary field
 Array field/List field/Dictionary field
 Matrix3 field
 Matrix3 field
@@ -12,7 +7,6 @@ Matrix4 field
 GameObject field
 GameObject field
 Resource field
 Resource field
 Component foldout field
 Component foldout field
-Text field
 
 
 -------------
 -------------
 Cursor
 Cursor
@@ -28,14 +22,50 @@ Other:
 
 
 -------------
 -------------
 
 
-When Inspector is displayed a SerializedObject of a Component (or some other type) is created
- - It will contain a list of SerializedFields, which you can call set/get on (setInt, setString, setObject, setArray, etc.)
- - It will also know its own type in an enum
- - You can query all SerializedFields of an SerializedObject or find individual ones by name
+TOMORROW:
+ - InspectableObject calls a C++ method which gets all SerializableFields, and C++ in turn
+   generates a list of InspectableFields
+     - They should have a type, name, and get/set methods
 
 
-You can call EditorGUI.SerializedField to automatically draw GUI depending on field type.
- - Or SerializedObject to draw GUI for entire object
+Dont forget to implement SceneObject.GetComponents
 
 
-------------
-PRIMARY advantage of handling properties like this, instead of making custom inspectors and calling GUI methods manually is that you get undo/redo support.
-ALSO I need tab movement
+Tab indexes
+ - EditorFields should have SetNextField method that allow you to set tab order directly
+ - InspectableField are automatically assigned ID
+   - ID is part depth (also governs identation) and part sequential index
+   - This allows me to add/remove elements from objects like lists and arrays and not mess up the tab order
+   - Also collapse/expand doesn't require us to modify tab indexes
+   - InspectableObjects should likely have a reference to the Inspector so that they can query next InspectableField based on tab ID
+   - These IDs are then used for calling SetNextField on normal EditorFields
+
+Custom inspector
+ - Custom inspector should have complete knowledge of the GUI objects within it (so that parent Inspector can expand/collapse it without worrying about the user forgetting to hide an element and messing up the other inspectors)
+ - Since custom inspector should have all of its elements in a specific GUILayout, we should be able to just manipulate that GUILayout
+
+Positioning/Indentation
+ - Inspector needs to be provided with a layout
+ - Whenever an InspectableObject is created it will create a new X layout with a space (for indenting) and a Y layout (for child elements)
+   - We will provide it with parent layout from which to create those on
+   - And depth for determining indent amount
+   - For top level objects there is no space or X layout (depth is 0)
+  - InspectableFields will use the Layouts they are given by their parent InspectableObjects
+
+C++ bit for creating InspectableField
+ - Need a way to list all fields in an object (should already have most of that functionality)
+ - And hook up those fields with get/set callbacks for editing/updating
+
+UndoRedo
+ - A global UndoRedo class for generic use
+ - Specific UndoRedo for automatic use in InspectableField
+ - Will likely need Undo/Redo context
+    - e.g. when in a text field I want to undo/redo my changes in that text field one by one
+    - but when I click outside maybe I just want to undo to the previous state before I even focused on that text field
+      - plus it doesn't make sense for example to undo a text field if I'm currently focusing on something in scene
+
+IMPLEMENTATION
+ - Add a Foldout GUI type and its C# version
+ - Attempt to display a list of Components on a SceneObject just by using the foldouts
+
+ - Add basic IntField and hook it up with InspectableField (get/set methods and everything)
+
+ - Add Multi-rank array, List & Dictionary types

+ 14 - 0
MBansheeEngine/Inspector/CustomInspector.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public abstract class CustomInspector
+    {
+        public abstract void Refresh();
+
+        public abstract void Destroy();
+    }
+}

+ 11 - 0
MBansheeEngine/Inspector/EditorField.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class EditorField
+    {
+    }
+}

+ 15 - 0
MBansheeEngine/Inspector/InspectableArray.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class InspectableArray : InspectableObjectBase
+    {
+        public InspectableArray(Array array)
+        {
+            // TODO - Populate "fields" array
+        }
+    }
+}

+ 65 - 0
MBansheeEngine/Inspector/InspectableField.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class InspectableField
+    {
+        public enum Type
+        {
+            Int,
+            Float,
+            Bool,
+            String,
+            Color,
+            Vector2,
+            Vector3,
+            Vector4,
+            GameObjectRef,
+            ResourceRef,
+            Object,
+            Array,
+            List,
+            Dictionary
+        }
+
+        private EditorField guiField; // TODO - This will likely be a specific EditorField type, not a generic base one
+        private InspectableObjectBase parent;
+        private Type type;
+
+        // TODO - Add getters/setters for all fields
+        
+        // TODO - Make sure "guiField" is created properly
+
+        public void Refresh()
+        {
+            // TODO - Update "guiField" from the field value by calling "Internal_Get*" method
+        }
+
+        public void Destroy()
+        {
+            // TODO - Called by the parent
+            //  Release "guiField"
+        }
+
+        private void OnValueChangedInt32(Int32 newValue)
+        {
+            // TODO - Callback from "guiField"
+            //  Need separate methods for all types
+            //  Call "Internal_Set*" method to update the actual field
+            //  Register an "Undo" action
+        }
+
+        // TODO - I need a set of these methods for all possible "Type"s
+        //  Internally they should use SerializableField methods for setting/getting values
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetInt32(IntPtr nativeInstance, Int32 value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Int32 Internal_GetInt32(IntPtr nativeInstance);
+    }
+}

+ 15 - 0
MBansheeEngine/Inspector/InspectableObject.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class InspectableObject : InspectableObjectBase
+    {
+        public InspectableObject(object obj)
+        {
+            // TODO - Populate "fields" array
+        }
+    }
+}

+ 44 - 0
MBansheeEngine/Inspector/InspectableObjectBase.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public abstract class InspectableObjectBase
+    {
+        private bool _isExpanded = false;
+        private InspectableField[] _fields;
+
+        public bool isExpanded { get { return _isExpanded; } }
+        public InspectableField[] fields { get { return _fields; } }
+
+        public void Expand()
+        {
+            // TODO - Show all child "fields"
+            //  Reposition all visual child elements
+            //  Re-do tab indexes
+            _isExpanded = true;
+        }
+
+        public void Collapse()
+        {
+            // TODO - Hide all child "fields"
+            //  Reposition all visual child elements
+            //  Re-do tab indexes
+            _isExpanded = false;
+        }
+
+        public void Refresh()
+        {
+            for (int i = 0; i < fields.Length; i++)
+                fields[i].Refresh();
+        }
+
+        public void Destroy()
+        {
+            for (int i = 0; i < fields.Length; i++)
+                fields[i].Destroy();
+        }
+    }
+}

+ 76 - 0
MBansheeEngine/Inspector/Inspector.cs

@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class Inspector
+    {
+        private GUIElement[] guiElements;
+        private InspectableObject[] childObjects;
+        private CustomInspector[] customInspectors;
+
+        public Inspector(SceneObject so, GUILayout layout)
+        {
+            // TODO - Create SceneObject gui elements (name + transform)
+            
+            List<CustomInspector> customInspectors = new List<CustomInspector>();
+            List<InspectableObject> childObjects = new List<InspectableObject>();
+
+            Component[] allComponents = so.GetComponents();
+            for (int i = 0; i < allComponents.Length; i++)
+            {
+                // TODO
+                //  - Create component foldout
+                //  - Hook up the foldout so when clicked it will expand/collapse the custom inspector or child object
+
+                if (HasCustomInspector(allComponents[i].GetType()))
+                {
+                    customInspectors.Add(CreateCustomInspector(allComponents[i]));
+                }
+                else
+                {
+                    childObjects.Add(new InspectableObject(allComponents[i]));
+                }
+            }
+
+            this.customInspectors = customInspectors.ToArray();
+            this.childObjects = childObjects.ToArray();
+        }
+
+        public void Refresh()
+        {
+            for (int i = 0; i < childObjects.Length; i++)
+                childObjects[i].Refresh();
+
+            for (int i = 0; i < customInspectors.Length; i++)
+                customInspectors[i].Refresh();
+        }
+
+        public void Destroy()
+        {
+            // TODO - Destroy SceneObject GUI elements
+
+            for (int i = 0; i < childObjects.Length; i++)
+                childObjects[i].Destroy();
+
+            for (int i = 0; i < customInspectors.Length; i++)
+                customInspectors[i].Destroy();
+        }
+
+        private bool HasCustomInspector(Type type)
+        {
+            // TODO - Check if Component (or some other type) has a custom inspector
+            return false;
+        }
+
+        private CustomInspector CreateCustomInspector(Component component)
+        {
+            // Find and create a custom inspector
+            return null;
+        }
+
+        // TODO - Ensure all GUI elements are properly cleaned up when this is destroyed
+    }
+}

+ 7 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -74,6 +74,13 @@
     <Compile Include="GUI\GUIToggle.cs" />
     <Compile Include="GUI\GUIToggle.cs" />
     <Compile Include="GUI\GUIToggleGroup.cs" />
     <Compile Include="GUI\GUIToggleGroup.cs" />
     <Compile Include="HideInInspector.cs" />
     <Compile Include="HideInInspector.cs" />
+    <Compile Include="Inspector\CustomInspector.cs" />
+    <Compile Include="Inspector\EditorField.cs" />
+    <Compile Include="Inspector\InspectableArray.cs" />
+    <Compile Include="Inspector\InspectableField.cs" />
+    <Compile Include="Inspector\InspectableObject.cs" />
+    <Compile Include="Inspector\InspectableObjectBase.cs" />
+    <Compile Include="Inspector\Inspector.cs" />
     <Compile Include="LocString.cs" />
     <Compile Include="LocString.cs" />
     <Compile Include="Math\MathEx.cs" />
     <Compile Include="Math\MathEx.cs" />
     <Compile Include="Math\Matrix3.cs" />
     <Compile Include="Math\Matrix3.cs" />

+ 5 - 0
MBansheeEngine/SceneObject.cs

@@ -33,6 +33,11 @@ namespace BansheeEngine
             return (T)Component.Internal_GetComponent(this, typeof(T));
             return (T)Component.Internal_GetComponent(this, typeof(T));
         }
         }
 
 
+        public Component[] GetComponents()
+        {
+            throw new NotImplementedException();
+        }
+
         public void RemoveComponent<T>() where T : Component
         public void RemoveComponent<T>() where T : Component
         {
         {
             Component.Internal_RemoveComponent(this, typeof(T));
             Component.Internal_RemoveComponent(this, typeof(T));

+ 6 - 0
Notes.txt

@@ -55,6 +55,12 @@ Reminders:
 	  considering that most people will definitely won't be writing new script systems.
 	  considering that most people will definitely won't be writing new script systems.
   - Perhaps add code generation functionality to the engine through Mono? I know Mono has support for it though its Embed interface
   - Perhaps add code generation functionality to the engine through Mono? I know Mono has support for it though its Embed interface
   - Add instancing to engine: All I really need to add are per-instance attributes in VertexData (and MeshData). And then RenderSystem::renderInstance method that also accepts an instance count.
   - Add instancing to engine: All I really need to add are per-instance attributes in VertexData (and MeshData). And then RenderSystem::renderInstance method that also accepts an instance count.
+  - Debug console
+    - Add ability to add colors tags like <color=#123>
+	- When showing a debug message, also provide a (clickable?) reference to Component it was triggered on (if applicable)
+	  - It really helps when you get an error on a Component that hundreds of SceneObjects use
+	- When displaying an error with a callstack, make each line of the callstack clickable where it opens the external editor
+
 
 
 Potential optimizations:
 Potential optimizations:
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.