ソースを参照

Feature: 'Create' button in object inspector will now offer a list of all derived types that can be assigned to that field

BearishSun 7 年 前
コミット
1779461ac5

+ 65 - 5
Source/Scripting/MBansheeEditor/Windows/Inspector/InspectableObject.cs

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System;
 using System;
+using System.Reflection;
 using BansheeEngine;
 using BansheeEngine;
 
 
 namespace BansheeEditor
 namespace BansheeEditor
@@ -25,6 +26,8 @@ namespace BansheeEditor
         private GUILayoutX guiChildLayout;
         private GUILayoutX guiChildLayout;
         private GUILayoutX guiTitleLayout;
         private GUILayoutX guiTitleLayout;
         private GUILayoutX guiInternalTitleLayout;
         private GUILayoutX guiInternalTitleLayout;
+        private GUIButton guiCreateBtn;
+        private ContextMenu createContextMenu;
         private bool isExpanded;
         private bool isExpanded;
         private bool forceUpdate = true;
         private bool forceUpdate = true;
         private State state;
         private State state;
@@ -44,6 +47,20 @@ namespace BansheeEditor
             : base(parent, title, path, SerializableProperty.FieldType.Object, depth, layout, property)
             : base(parent, title, path, SerializableProperty.FieldType.Object, depth, layout, property)
         {
         {
             isExpanded = parent.Persistent.GetBool(path + "_Expanded");
             isExpanded = parent.Persistent.GetBool(path + "_Expanded");
+
+            // Builds a context menu that lets the user create objects to assign to this field.
+            Type[] types = GetInstantiableTypes(property.InternalType);
+            if (types.Length > 0)
+            {
+                createContextMenu = new ContextMenu();
+
+                Array.Sort(types, (x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
+                foreach (var type in types)
+                {
+                    createContextMenu.AddItem(type.Namespace + "::" + type.Name,
+                        () => property.SetValue(Activator.CreateInstance(type)));
+                }
+            }
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -104,9 +121,9 @@ namespace BansheeEditor
                 {
                 {
                     GUIContent createIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Create), 
                     GUIContent createIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Create), 
                         new LocEdString("Create"));
                         new LocEdString("Create"));
-                    GUIButton createBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30));
-                    createBtn.OnClick += OnCreateButtonClicked;
-                    guiInternalTitleLayout.AddElement(createBtn);
+                    guiCreateBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30));
+                    guiCreateBtn.OnClick += OnCreateButtonClicked;
+                    guiInternalTitleLayout.AddElement(guiCreateBtn);
                 }
                 }
             };
             };
 
 
@@ -193,6 +210,8 @@ namespace BansheeEditor
                if (propertyValue != null)
                if (propertyValue != null)
                {
                {
                    guiInternalTitleLayout.Destroy();
                    guiInternalTitleLayout.Destroy();
+                   guiCreateBtn = null;
+
                    BuildFilledGUI();
                    BuildFilledGUI();
                    state = State.Filled;
                    state = State.Filled;
                }
                }
@@ -204,8 +223,9 @@ namespace BansheeEditor
 
 
                children.Clear();
                children.Clear();
                guiInternalTitleLayout.Destroy();
                guiInternalTitleLayout.Destroy();
+                guiCreateBtn = null;
 
 
-               if (guiChildLayout != null)
+                if (guiChildLayout != null)
                {
                {
                    guiChildLayout.Destroy();
                    guiChildLayout.Destroy();
                    guiChildLayout = null;
                    guiChildLayout = null;
@@ -251,7 +271,13 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         private void OnCreateButtonClicked()
         private void OnCreateButtonClicked()
         {
         {
-            property.SetValue(property.CreateObjectInstance<object>());
+            if (createContextMenu == null)
+                return;
+
+            Rect2I bounds = GUIUtility.CalculateBounds(guiCreateBtn, guiInternalTitleLayout);
+            Vector2I position = new Vector2I(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
+
+            createContextMenu.Open(position, guiInternalTitleLayout);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -272,6 +298,40 @@ namespace BansheeEditor
             Empty,
             Empty,
             Filled
             Filled
         }
         }
+
+        /// <summary>
+        /// Returns a list of all types that can be created using the parameterless constructor and assigned to an object of
+        /// type <paramref name="type"/>.
+        /// </summary>
+        /// <param name="type">Type to which the instantiable types need to be assignable to.</param>
+        /// <returns>List of types that can be instantiated and assigned type <paramref name="type"/></returns>
+        private static Type[] GetInstantiableTypes(Type type)
+        {
+            // Note: This could be cached
+            List<Type> output = new List<Type>();
+
+            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
+            foreach (var assembly in assemblies)
+            {
+                Type[] assemblyTypes = assembly.GetExportedTypes();
+                foreach (var assemblyType in assemblyTypes)
+                {
+                    if (assemblyType.IsAbstract)
+                        continue;
+
+                    if (!type.IsAssignableFrom(assemblyType))
+                        continue;
+
+                    var ctor = assemblyType.GetConstructor(Type.EmptyTypes);
+                    if (ctor == null)
+                        continue;
+
+                    output.Add(assemblyType);
+                }
+            }
+
+            return output.ToArray();
+        }
     }
     }
 
 
     /** @} */
     /** @} */

+ 4 - 4
Source/Scripting/MBansheeEngine/GUI/GUIUtility.cs

@@ -31,11 +31,11 @@ namespace BansheeEngine
         /// returns bounds relative to a specific parent GUI panel.
         /// returns bounds relative to a specific parent GUI panel.
         /// </summary>
         /// </summary>
         /// <param name="element">Elements to calculate the bounds for.</param>
         /// <param name="element">Elements to calculate the bounds for.</param>
-        /// <param name="relativeTo">GUI panel the bounds will be relative to. If this is null or the provided panel is not
-        ///                          a parent of the provided GUI element, the returned bounds will be relative to the 
+        /// <param name="relativeTo">GUI layout the bounds will be relative to. If this is null or the provided layout is
+        ///                          not a parent of the provided GUI element, the returned bounds will be relative to the 
         ///                          first GUI panel parent instead.</param>
         ///                          first GUI panel parent instead.</param>
-        /// <returns>Bounds of a GUI element relative to the provided GUI panel.</returns>
-        public static Rect2I CalculateBounds(GUIElement element, GUIPanel relativeTo = null)
+        /// <returns>Bounds of a GUI element relative to the provided GUI layout.</returns>
+        public static Rect2I CalculateBounds(GUIElement element, GUILayout relativeTo = null)
         {
         {
             IntPtr relativeToNative = IntPtr.Zero;
             IntPtr relativeToNative = IntPtr.Zero;
             if (relativeTo != null)
             if (relativeTo != null)