Просмотр исходного кода

Animation editor field display and field selection WIP

BearishSun 9 лет назад
Родитель
Сommit
7896f9f7f4

+ 11 - 0
Source/MBansheeEditor/Utility/EditorBuiltin.cs

@@ -95,6 +95,14 @@ namespace BansheeEditor
             get { return Internal_GetDefaultFont(); }
         }
 
+        /// <summary>
+        /// Returns the default GUI skin used in the editor.
+        /// </summary>
+        public static GUISkin GUISkin
+        {
+            get { return Internal_GetGUISkin(); }
+        }
+
         /// <summary>
         /// Retrieves an icon used for displaying an entry in the library window.
         /// </summary>
@@ -211,6 +219,9 @@ namespace BansheeEditor
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Font Internal_GetDefaultFont();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern GUISkin Internal_GetGUISkin();
     }
 
     /** @} */

+ 2 - 10
Source/MBansheeEditor/Windows/Animation/FieldSelectionWindow.cs

@@ -22,19 +22,11 @@ namespace BansheeEditor
         /// </summary>
         public FieldSelectionWindow()
         {
-            GUILayout layoutY = GUI.AddLayoutY();
-            layoutY.AddSpace(5);
-            GUILayout layoutX = layoutY.AddLayoutX();
-            layoutY.AddSpace(5);
-
-            layoutX.AddSpace(5);
-            GUILayout selectorLayout = layoutX.AddLayoutY();
-            layoutX.AddSpace(5);
-
-            guiFieldSelector = new GUIFieldSelector(selectorLayout, Selection.SceneObject);
+            guiFieldSelector = new GUIFieldSelector(GUI, Selection.SceneObject, Width, Height);
             guiFieldSelector.OnElementSelected += (so, comp, path, type) =>
             {
                 OnFieldSelected?.Invoke(path, type);
+                Close();
             };
         }
     }

+ 140 - 36
Source/MBansheeEditor/Windows/Animation/GUIAnimFieldDisplay.cs

@@ -27,13 +27,13 @@ namespace BansheeEditor
         {
             this.root = root;
 
-            scrollArea = new GUIScrollArea();
+            scrollArea = new GUIScrollArea(ScrollBarType.ShowIfDoesntFit, ScrollBarType.NeverShow);
             layout.AddElement(scrollArea);
 
             SetSize(width, height);
         }
 
-        public Action<string, bool> OnSelectionChanged;
+        public Action<string> OnEntrySelected;
 
         public void SetSize(int width, int height)
         {
@@ -77,12 +77,46 @@ namespace BansheeEditor
             }
         }
 
+        public void SetSelection(string[] paths)
+        {
+            Action<GUIAnimFieldEntry> updateSelection = field =>
+            {
+                bool foundSelected = false;
+                for (int j = 0; j < paths.Length; j++)
+                {
+                    if (field.Path == paths[j])
+                    {
+                        field.SetSelection(true);
+                        foundSelected = true;
+                        break;
+                    }
+                }
+
+                if (!foundSelected)
+                    field.SetSelection(false);
+            };
+
+            for (int i = 0; i < fields.Length; i++)
+            {
+                updateSelection(fields[i]);
+
+                // Check children (only one level allowed)
+                GUIAnimFieldEntry[] children = fields[i].GetChildren();
+                if (children == null)
+                    continue;
+
+                for (int j = 0; j < children.Length; j++)
+                    updateSelection(children[j]);
+            }
+        }
+
         private SerializableProperty FindProperty(string path)
         {
             if (string.IsNullOrEmpty(path) || root == null)
                 return null;
 
-            string[] entries = path.Split('/');
+            string trimmedPath = path.Trim('/');
+            string[] entries = trimmedPath.Split('/');
 
             // Find scene object referenced by the path
             SceneObject so = null;
@@ -203,10 +237,23 @@ namespace BansheeEditor
             GUIPanel mainPanel = rootPanel.AddPanel();
             GUIPanel underlayPanel = rootPanel.AddPanel(1);
             GUIPanel overlayPanel = rootPanel.AddPanel(-1);
+            GUIPanel backgroundPanel = rootPanel.AddPanel(2);
 
             layouts.main = mainPanel.AddLayoutY();
             layouts.underlay = underlayPanel.AddLayoutY();
             layouts.overlay = overlayPanel.AddLayoutY();
+            layouts.background = backgroundPanel.AddLayoutY();
+
+            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
+            catchAll.Bounds = new Rect2I(0, 0, width, height);
+            catchAll.OnClick += () => OnEntrySelected(null);
+
+            underlayPanel.AddElement(catchAll);
+
+            layouts.main.AddSpace(5);
+            layouts.underlay.AddSpace(5);
+            layouts.overlay.AddSpace(5);
+            layouts.background.AddSpace(5);
 
             fields = new GUIAnimFieldEntry[paths.Count];
             for (int i = 0; i < paths.Count; i++)
@@ -234,19 +281,25 @@ namespace BansheeEditor
                             fields[i] = new GUIAnimSimpleEntry(layouts, paths[i]);
                             break;
                     }
-
-                    if (fields[i] != null)
-                        fields[i].OnSelectionChanged += OnSelectionChanged;
                 }
                 else
                 {
                     fields[i] = new GUIAnimMissingEntry(layouts, paths[i]);
                 }
+
+                if (fields[i] != null)
+                    fields[i].OnEntrySelected += OnEntrySelected;
             }
 
+            layouts.main.AddSpace(5);
+            layouts.underlay.AddSpace(5);
+            layouts.overlay.AddSpace(5);
+            layouts.background.AddSpace(5);
+
             layouts.main.AddFlexibleSpace();
             layouts.underlay.AddFlexibleSpace();
             layouts.overlay.AddFlexibleSpace();
+            layouts.background.AddFlexibleSpace();
         }
     }
 
@@ -255,6 +308,7 @@ namespace BansheeEditor
         public GUILayout main;
         public GUILayout underlay;
         public GUILayout overlay;
+        public GUILayout background;
     }
 
     internal struct GUIAnimFieldPathValue
@@ -269,42 +323,67 @@ namespace BansheeEditor
         protected const int INDENT_AMOUNT = 10;
 
         protected string path;
-        private GUIToggle toggle;
+        private GUIButton selectionBtn;
+        private GUITexture backgroundTexture;
 
         private int entryHeight;
 
-        public Action<string, bool> OnSelectionChanged;
+        public Action<string> OnEntrySelected;
 
         public string Path { get { return path; } }
 
-        public GUIAnimFieldEntry(GUIAnimFieldLayouts layouts, string path)
+        public GUIAnimFieldEntry(GUIAnimFieldLayouts layouts, string path, bool shortName)
         {
             this.path = path;
 
             GUILayoutX toggleLayout = layouts.main.AddLayoutX();
             toggleLayout.AddSpace(15);
 
-            toggle = new GUIToggle(GetDisplayName(path), EditorStyles.SelectableLabel, GUIOption.FlexibleWidth());
-            toggle.OnToggled += x => { OnSelectionChanged?.Invoke(path, x); };
+            selectionBtn = new GUIButton(GetDisplayName(path, shortName), EditorStyles.Label, GUIOption.FlexibleWidth());
+            selectionBtn.OnClick += () =>
+            {
+                OnEntrySelected?.Invoke(path);
+            };
+
+            toggleLayout.AddElement(selectionBtn);
 
-            toggleLayout.AddElement(toggle);
+            entryHeight = selectionBtn.Bounds.height;
 
-            entryHeight = toggle.Bounds.height;
+            backgroundTexture = new GUITexture(Builtin.WhiteTexture, GUITextureScaleMode.StretchToFit, 
+                GUIOption.FlexibleWidth());
+            backgroundTexture.SetTint(Color.Transparent);
+            backgroundTexture.SetHeight(entryHeight);
+
+            layouts.background.AddElement(backgroundTexture);
         }
 
         public virtual void Toggle(bool on)
         {
-            toggle.Active = on;
+            selectionBtn.Active = on;
+            backgroundTexture.Active = on;
+        }
+
+        public void SetSelection(bool selected)
+        {
+            if(selected)
+                backgroundTexture.SetTint(Color.DarkCyan);
+            else
+                backgroundTexture.SetTint(Color.Transparent);
         }
 
         public virtual void SetValue(object value) { }
 
+        public virtual GUIAnimFieldEntry[] GetChildren()
+        {
+            return null;
+        }
+
         protected int GetEntryHeight()
         {
             return entryHeight;
         }
 
-        protected static string GetDisplayName(string path)
+        protected static string GetDisplayName(string path, bool shortName)
         {
             if (string.IsNullOrEmpty(path))
                 return "";
@@ -313,24 +392,30 @@ namespace BansheeEditor
             string compName;
             string propertyPath;
 
-            GetNames(path, out soName, out compName, out propertyPath);
+            string trimmedPath = path.Trim('/');
+            GetNames(trimmedPath, shortName, out soName, out compName, out propertyPath);
 
             if (soName == null || propertyPath == null)
                 return "";
 
-            string truncatedPropPath;
-            if (propertyPath.Length > MAX_PATH_LENGTH)
-                truncatedPropPath = "..." + propertyPath.Substring(propertyPath.Length - MAX_PATH_LENGTH);
+            if (shortName)
+                return propertyPath;
             else
-                truncatedPropPath = propertyPath;
+            {
+                string truncatedPropPath;
+                if (propertyPath.Length > MAX_PATH_LENGTH)
+                    truncatedPropPath = "..." + propertyPath.Substring(propertyPath.Length - MAX_PATH_LENGTH);
+                else
+                    truncatedPropPath = propertyPath;
 
-            if (compName != null)
-                return soName + "(" + compName + ")." + truncatedPropPath;
-            else
-                return soName + "." + truncatedPropPath;
+                if (compName != null)
+                    return soName + "(" + compName + ") - " + truncatedPropPath;
+                else
+                    return soName + " - " + truncatedPropPath;
+            }
         }
 
-        protected static void GetNames(string path, out string soName, out string compName, out string propertyPath)
+        protected static void GetNames(string path, bool shortName, out string soName, out string compName, out string propertyPath)
         {
             string[] entries = path.Split('/');
 
@@ -386,11 +471,24 @@ namespace BansheeEditor
                 }
             }
 
-            StringBuilder pathBuilder = new StringBuilder();
-            for (; pathIdx < entries.Length; pathIdx++)
-                pathBuilder.Append(entries[pathIdx] + "/");
+            if (shortName)
+            {
+                if (pathIdx < entries.Length)
+                    propertyPath = entries[entries.Length - 1];
+                else
+                    propertyPath = null;
+            }
+            else
+            {
+                StringBuilder pathBuilder = new StringBuilder();
+                for (; pathIdx < entries.Length - 1; pathIdx++)
+                    pathBuilder.Append(entries[pathIdx] + "/");
+
+                if (pathIdx < entries.Length)
+                    pathBuilder.Append(entries[pathIdx]);
 
-            propertyPath = pathBuilder.ToString();
+                propertyPath = pathBuilder.ToString();
+            }
         }
     }
 
@@ -400,8 +498,8 @@ namespace BansheeEditor
         private GUILayoutX underlayLayout;
         private GUILabel overlaySpacing;
 
-        public GUIAnimSimpleEntry(GUIAnimFieldLayouts layouts, string path)
-            : base(layouts, path)
+        public GUIAnimSimpleEntry(GUIAnimFieldLayouts layouts, string path, bool child = false)
+            : base(layouts, path, child)
         {
             valueDisplay = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
             underlayLayout = layouts.underlay.AddLayoutX();
@@ -411,8 +509,6 @@ namespace BansheeEditor
 
             overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
             layouts.overlay.AddElement(overlaySpacing);
-
-            // TODO - Alternating backgrounds
         }
 
         public override void Toggle(bool on)
@@ -442,7 +538,7 @@ namespace BansheeEditor
         protected GUIAnimSimpleEntry[] children;
 
         public GUIAnimComplexEntry(GUIAnimFieldLayouts layouts, string path, string[] childEntries)
-            : base(layouts, path)
+            : base(layouts, path, false)
         {
             foldout = new GUIToggle("", EditorStyles.Expand);
             foldout.OnToggled += Toggle;
@@ -456,7 +552,10 @@ namespace BansheeEditor
 
             children = new GUIAnimSimpleEntry[childEntries.Length];
             for (int i = 0; i < childEntries.Length; i++)
-                children[i] = new GUIAnimSimpleEntry(layouts, path + childEntries[i]);
+            {
+                children[i] = new GUIAnimSimpleEntry(layouts, path + childEntries[i], true);
+                children[i].OnEntrySelected += x => { OnEntrySelected?.Invoke(x); };
+            }
 
             Toggle(false);
         }
@@ -466,6 +565,11 @@ namespace BansheeEditor
             foreach(var child in children)
                 child.Toggle(on);
         }
+
+        public override GUIAnimFieldEntry[] GetChildren()
+        {
+            return children;
+        }
     }
 
     internal class GUIAnimVec2Entry : GUIAnimComplexEntry
@@ -548,7 +652,7 @@ namespace BansheeEditor
         private GUILabel overlaySpacing;
 
         public GUIAnimMissingEntry(GUIAnimFieldLayouts layouts, string path)
-            : base(layouts, path)
+            : base(layouts, path, false)
         {
             missingLabel = new GUILabel("Missing property!", GUIOption.FixedHeight(GetEntryHeight()));
             underlayLayout = layouts.underlay.AddLayoutX();

+ 27 - 9
Source/MBansheeEditor/Windows/Animation/GUIFieldSelector.cs

@@ -18,8 +18,10 @@ namespace BansheeEditor
     public class GUIFieldSelector
     {
         private const int INDENT_AMOUNT = 5;
+        private const int PADDING = 5;
 
-        private GUILayoutY mainLayout;
+        private GUIScrollArea scrollArea;
+        private int foldoutWidth;
 
         private SceneObject rootSO;
         private Element rootElement;
@@ -47,8 +49,8 @@ namespace BansheeEditor
             public string path;
 
             public GUIToggle toggle;
-            public GUILayoutY childLayout;
-            public GUILayoutX indentLayout;
+            public GUILayout childLayout;
+            public GUILayout indentLayout;
 
             public Element[] children;
         }
@@ -68,10 +70,21 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="layout">Layout into which to add the selector GUI hierarchy.</param>
         /// <param name="so">Scene object to inspect the fields for.</param>
-        public GUIFieldSelector(GUILayout layout, SceneObject so)
+        /// <param name="width">Width of the selector area, in pixels.</param>
+        /// <param name="height">Height of the selector area, in pixels.</param>
+        public GUIFieldSelector(GUILayout layout, SceneObject so, int width, int height)
         {
             rootSO = so;
-            mainLayout = layout.AddLayoutY();
+
+            scrollArea = new GUIScrollArea();
+            scrollArea.SetWidth(width);
+            scrollArea.SetHeight(height);
+
+            layout.AddElement(scrollArea);
+
+            GUISkin skin = EditorBuiltin.GUISkin;
+            GUIElementStyle style = skin.GetStyle(EditorStyles.Expand);
+            foldoutWidth = style.Width;
 
             Rebuild();
         }
@@ -81,18 +94,20 @@ namespace BansheeEditor
         /// </summary>
         private void Rebuild()
         {
-            mainLayout.Clear();
+            scrollArea.Layout.Clear();
             rootElement = new Element();
 
             if (rootSO == null)
                 return;
 
             rootElement.so = rootSO;
-            rootElement.childLayout = mainLayout;
+            rootElement.childLayout = scrollArea.Layout;
             rootElement.indentLayout = null;
 
+            scrollArea.Layout.AddSpace(5);
             AddSceneObjectRows(rootElement);
-            mainLayout.AddFlexibleSpace();
+            scrollArea.Layout.AddSpace(5);
+            scrollArea.Layout.AddFlexibleSpace();
         }
 
         /// <summary>
@@ -198,7 +213,9 @@ namespace BansheeEditor
             Element element = new Element(so, component, path);
 
             GUILayoutX elementLayout = layout.AddLayoutX();
-           
+
+            elementLayout.AddSpace(PADDING);
+            elementLayout.AddSpace(foldoutWidth);
             GUILabel label = new GUILabel(new LocEdString(name));
             elementLayout.AddElement(label);
 
@@ -237,6 +254,7 @@ namespace BansheeEditor
             element.toggle = new GUIToggle("", EditorStyles.Expand);
             element.toggle.OnToggled += x => toggleCallback(element, x);
 
+            foldoutLayout.AddSpace(PADDING);
             foldoutLayout.AddElement(element.toggle);
 
             if (icon != null)

+ 30 - 6
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -82,7 +82,7 @@ namespace BansheeEditor
             if (!isInitialized)
                 return;
 
-            guiFieldDisplay.SetSize(width, height - buttonLayoutHeight*2);
+            guiFieldDisplay.SetSize(FIELD_DISPLAY_WIDTH, height - buttonLayoutHeight*2);
 
             int curveEditorWidth = Math.Max(0, width - FIELD_DISPLAY_WIDTH);
             guiCurveEditor.SetSize(curveEditorWidth, height - buttonLayoutHeight);
@@ -186,7 +186,7 @@ namespace BansheeEditor
 
             guiFieldDisplay = new GUIAnimFieldDisplay(fieldDisplayLayout, FIELD_DISPLAY_WIDTH,
                 Height - buttonLayoutHeight * 2, selectedSO);
-            guiFieldDisplay.OnSelectionChanged += OnFieldSelectionChanged;
+            guiFieldDisplay.OnEntrySelected += OnFieldSelected;
 
             GUILayout bottomButtonLayout = fieldDisplayLayout.AddLayoutX();
             bottomButtonLayout.AddElement(addPropertyBtn);
@@ -314,12 +314,36 @@ namespace BansheeEditor
             UpdateDisplayedCurves();
         }
 
-        private void OnFieldSelectionChanged(string path, bool selected)
+        private bool IsPathParent(string child, string parent)
         {
-            if (selected)
+            string[] childEntries = child.Split('/', '.');
+            string[] parentEntries = parent.Split('/', '.');
+
+            if (parentEntries.Length >= child.Length)
+                return false;
+
+            int compareLength = Math.Min(childEntries.Length, parentEntries.Length);
+            for (int i = 0; i < compareLength; i++)
+            {
+                if (childEntries[i] != parentEntries[i])
+                    return false;
+            }
+
+            return true;
+        }
+
+        private void OnFieldSelected(string path)
+        {
+            if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
+                selectedFields.Clear();
+
+            if (!string.IsNullOrEmpty(path))
+            {
+                selectedFields.RemoveAll(x => { return x == path || IsPathParent(x, path); });
                 selectedFields.Add(path);
-            else
-                selectedFields.Remove(path);
+            }
+
+            guiFieldDisplay.SetSelection(selectedFields.ToArray());
 
             UpdateDisplayedCurves();
         }

+ 1 - 1
Source/MBansheeEditor/Windows/Scene/SceneCamera.cs

@@ -266,7 +266,7 @@ namespace BansheeEditor
             }
 
             SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
-            if (sceneWindow.Active)
+            if (sceneWindow.Active && sceneWindow.HasFocus)
             {
                 Rect2I bounds = sceneWindow.Bounds;
 

+ 2 - 1
Source/MBansheeEngine/Serialization/SerializableObject.cs

@@ -95,7 +95,8 @@ namespace BansheeEngine
             if (path == null)
                 return null;
 
-            string[] pathEntries = path.Split('/');
+            string trimmedPath = path.Trim('/');
+            string[] pathEntries = trimmedPath.Split('/');
             PropertyPathElement[] pathElements = new PropertyPathElement[pathEntries.Length];
             for (int i = 0; i < pathEntries.Length; i++)
             {

+ 2 - 0
Source/MBansheeEngine/Serialization/SerializableProperty.cs

@@ -352,6 +352,8 @@ namespace BansheeEngine
                     return FieldType.Vector3;
                 else if (internalType == typeof (Vector4))
                     return FieldType.Vector4;
+                else if (internalType == typeof(Quaternion))
+                    return FieldType.Vector4;
                 else if (internalType == typeof (Color))
                     return FieldType.Color;
                 else if (internalType.IsSubclassOf(typeof (GameObject)))

+ 1 - 0
Source/MBansheeEngine/Utility/Color.cs

@@ -33,6 +33,7 @@ namespace BansheeEngine
         public static Color DarkGray { get { return new Color(63.0f / 255.0f, 63.0f / 255.0f, 63.0f / 255.0f, 1.0f); } }
         public static Color LightGray { get { return new Color(200.0f / 255.0f, 200.0f / 255.0f, 200.0f / 255.0f, 1.0f); } }
         public static Color BansheeOrange { get { return new Color(1.0f, (168.0f/255.0f), 0.0f, 1.0f); } }
+        public static Color Transparent { get { return new Color(0.0f, 0.0f, 0.0f, 0.0f); } }
 
         /// <summary>
         /// Accesses color components by an index.

+ 1 - 0
Source/SBansheeEditor/Include/BsScriptEditorBuiltin.h

@@ -38,6 +38,7 @@ namespace BansheeEngine
 		static MonoObject* internal_GetEditorIcon(EditorIcon icon);
 
 		static MonoObject* internal_GetDefaultFont();
+		static MonoObject* internal_GetGUISkin();
 	};
 
 	/** @} */

+ 12 - 0
Source/SBansheeEditor/Source/BsScriptEditorBuiltin.cpp

@@ -9,6 +9,7 @@
 #include "BsScriptGUIContentImages.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptFont.h"
+#include "BsScriptGUISkin.h"
 
 namespace BansheeEngine
 {
@@ -29,6 +30,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetLogIcon", &ScriptEditorBuiltin::internal_GetLogIcon);
 		metaData.scriptClass->addInternalCall("Internal_GetEditorIcon", &ScriptEditorBuiltin::internal_GetEditorIcon);
 		metaData.scriptClass->addInternalCall("Internal_GetDefaultFont", &ScriptEditorBuiltin::internal_GetDefaultFont);
+		metaData.scriptClass->addInternalCall("Internal_GetGUISkin", &ScriptEditorBuiltin::internal_GetGUISkin);
 	}
 
 	MonoObject* ScriptEditorBuiltin::internal_getLibraryItemIcon(ProjectIcon icon, int size)
@@ -110,4 +112,14 @@ namespace BansheeEngine
 
 		return scriptFont->getManagedInstance();
 	}
+
+	MonoObject* ScriptEditorBuiltin::internal_GetGUISkin()
+	{
+		HGUISkin guiSkin = BuiltinEditorResources::instance().getSkin();
+
+		ScriptGUISkin* scriptGUISkin;
+		ScriptResourceManager::instance().getScriptResource(guiSkin, &scriptGUISkin, true);
+
+		return scriptGUISkin->getManagedInstance();
+	}
 }