Bläddra i källkod

Added a GUIArray object to simplify creating generic GUI for arrays (Not tested)
Added FontInspector & PrefabInspector

BearishSun 10 år sedan
förälder
incheckning
011fa7a847

+ 510 - 0
MBansheeEditor/GUI/GUIArray.cs

@@ -0,0 +1,510 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Base class for objects that display GUI for a modifyable list of elements. Elements can be added, removed and moved.
+    /// </summary>
+    public abstract class GUIListBase
+    {
+        private const int IndentAmount = 5;
+
+        protected IList list;
+        protected Type listType;
+
+        protected List<GUIListRow> rows = new List<GUIListRow>();
+        protected GUIIntField guiSizeField;
+        protected GUILayoutX guiChildLayout;
+        protected GUILayoutX guiTitleLayout;
+        protected bool isExpanded;
+
+        /// <summary>
+        /// Constructs a new GUI list.
+        /// </summary>
+        protected GUIListBase()
+        { }
+
+        /// <summary>
+        /// Constructs a new GUI list with the specified row types. Must be called right after the constructor.
+        /// </summary>
+        /// <typeparam name="T">Type of rows that are used to handle GUI for individual list elements.</typeparam>
+        /// <param name="title">Label to display on the list GUI title.</param>
+        /// <param name="list">Object containing the list data. Can be null.</param>
+        /// <param name="listType">Type of the <paramref name="list"/> parameter. Needs to be specified in case that
+        ///                        parameter is null.</param>
+        /// <param name="layout">Layout to which to append the list GUI elements to.</param>
+        protected void Construct<T>(LocString title, IList list, Type listType, GUILayout layout) where T : GUIListRow, new()
+        {
+            this.list = list;
+            this.listType = listType;
+
+            if (list == null)
+            {
+                guiChildLayout = null;
+                guiTitleLayout = layout.AddLayoutX();
+
+                guiTitleLayout.AddElement(new GUILabel(title));
+                guiTitleLayout.AddElement(new GUILabel("Empty", GUIOption.FixedWidth(100)));
+
+                GUIButton createBtn = new GUIButton("Cr", GUIOption.FixedWidth(20));
+                createBtn.OnClick += OnCreateButtonClicked;
+                guiTitleLayout.AddElement(createBtn);
+            }
+            else
+            {
+                GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
+                guiFoldout.Value = isExpanded;
+                guiFoldout.OnToggled += OnFoldoutToggled;
+                guiSizeField = new GUIIntField("", GUIOption.FixedWidth(50));
+                guiSizeField.SetRange(0, int.MaxValue);
+                GUIButton guiResizeBtn = new GUIButton("R", GUIOption.FixedWidth(20));
+                guiResizeBtn.OnClick += OnResizeButtonClicked;
+                GUIButton guiClearBtn = new GUIButton("Cl", GUIOption.FixedWidth(20));
+                guiClearBtn.OnClick += OnClearButtonClicked;
+
+                guiTitleLayout = layout.AddLayoutX();
+                guiTitleLayout.AddElement(guiFoldout);
+                guiTitleLayout.AddElement(guiSizeField);
+                guiTitleLayout.AddElement(guiResizeBtn);
+                guiTitleLayout.AddElement(guiClearBtn);
+
+                guiSizeField.Value = list.Count;
+
+                guiChildLayout = layout.AddLayoutX();
+                guiChildLayout.AddSpace(IndentAmount);
+                guiChildLayout.Visible = isExpanded;
+
+                GUIPanel guiContentPanel = guiChildLayout.AddPanel();
+                GUILayoutX guiIndentLayoutX = guiContentPanel.AddLayoutX();
+                guiIndentLayoutX.AddSpace(IndentAmount);
+                GUILayoutY guiIndentLayoutY = guiIndentLayoutX.AddLayoutY();
+                guiIndentLayoutY.AddSpace(IndentAmount);
+                GUILayoutY guiContentLayout = guiIndentLayoutY.AddLayoutY();
+                guiIndentLayoutY.AddSpace(IndentAmount);
+                guiIndentLayoutX.AddSpace(IndentAmount);
+                guiChildLayout.AddSpace(IndentAmount);
+
+                GUIPanel backgroundPanel = guiContentPanel.AddPanel(Inspector.START_BACKGROUND_DEPTH);
+                GUITexture inspectorContentBg = new GUITexture(null, EditorStyles.InspectorContentBg);
+                backgroundPanel.AddElement(inspectorContentBg);
+
+                for (int i = 0; i < list.Count; i++)
+                {
+                    GUIListRow newRow = new T();
+                    newRow.Update(this, guiContentLayout, i);
+
+                    rows.Add(newRow);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets a value of an element at the specified index in the list.
+        /// </summary>
+        /// <param name="seqIndex">Sequential index of the element whose value to retrieve.</param>
+        /// <returns>Value of the list element at the specified index.</returns>
+        protected internal abstract object GetValue(int seqIndex);
+
+        /// <summary>
+        /// Sets a value of an element at the specified index in the list.
+        /// </summary>
+        /// <param name="seqIndex">Sequential index of the element whose value to set.</param>
+        /// <param name="value">Value to assign to the element. Caller must ensure it is of valid type.</param>
+        protected internal abstract void SetValue(int seqIndex, object value);
+
+        /// <summary>
+        /// Triggered when the user clicks on the expand/collapse toggle in the title bar.
+        /// </summary>
+        /// <param name="expanded">Determines whether the contents were expanded or collapsed.</param>
+        private void OnFoldoutToggled(bool expanded)
+        {
+            isExpanded = expanded;
+
+            if (guiChildLayout != null)
+                guiChildLayout.Visible = isExpanded;
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks on the create button on the title bar. Creates a brand new list with zero
+        /// elements in the place of the current list.
+        /// </summary>
+        protected abstract void OnCreateButtonClicked();
+
+        /// <summary>
+        /// Triggered when the user clicks on the resize button on the title bar. Changes the size of the list while
+        /// preserving existing contents.
+        /// </summary>
+        protected abstract void OnResizeButtonClicked();
+
+        /// <summary>
+        /// Triggered when the user clicks on the clear button on the title bar. Deletes the current list object.
+        /// </summary>
+        protected abstract void OnClearButtonClicked();
+
+        /// <summary>
+        /// Triggered when the user clicks on the delete button next to the list entry. Deletes an element in the list.
+        /// </summary>
+        /// <param name="index">Sequential index of the element in the list to remove.</param>
+        protected internal abstract void OnDeleteButtonClicked(int index);
+
+        /// <summary>
+        /// Triggered when the user clicks on the clone button next to the list entry. Clones an element in the list and
+        /// adds the clone to the back of the list. Non-value types must implement the <see cref="ICloneable"/> interface 
+        /// in order to be cloned. If it doesn't, an empty object is created instead of a clone.
+        /// </summary>
+        /// <param name="index">Sequential index of the element in the list to clone.</param>
+        protected internal abstract void OnCloneButtonClicked(int index);
+
+        /// <summary>
+        /// Triggered when the user clicks on the move up button next to the list entry. Moves an element from the current
+        /// list index to the one right before it, if not at zero.
+        /// </summary>
+        /// <param name="index">Sequential index of the element in the list to move.</param>
+        protected internal abstract void OnMoveUpButtonClicked(int index);
+
+        /// <summary>
+        /// Triggered when the user clicks on the move down button next to the list entry. Moves an element from the current
+        /// list index to the one right after it, if the element isn't already the last element.
+        /// </summary>
+        /// <param name="index">Sequential index of the element in the list to move.</param>
+        protected internal abstract void OnMoveDownButtonClicked(int index);
+    }
+
+    /// <summary>
+    /// Creates GUI elements that allow viewing and manipulation of a <see cref="System.Array"/>. When constructing the
+    /// object user can provide a custom type that manages GUI for individual array elements.
+    /// </summary>
+    public class GUIArray : GUIListBase
+    {
+        /// <summary>
+        /// Triggered when the reference array has been changed. This does not include changes that only happen to its 
+        /// internal elements.
+        /// </summary>
+        public Action<Array> OnChanged;
+
+        /// <summary>
+        /// Array object whose contents are displayed.
+        /// </summary>
+        public Array Array { get { return (Array)list; } }
+
+        /// <summary>
+        /// Constructs a new GUI array.
+        /// </summary>
+        private GUIArray()
+        { }
+
+        /// <summary>
+        /// Creates a new GUI array.
+        /// </summary>
+        /// <typeparam name="T">Type of rows that are used to handle GUI for individual list elements.</typeparam>
+        /// <param name="title">Label to display on the list GUI title.</param>
+        /// <param name="array">Object containing the list data. Cannot be null.</param>
+        /// <param name="layout">Layout to which to append the list GUI elements to.</param>
+        public static GUIArray Create<T>(LocString title, Array array, GUILayout layout) where T : GUIListRow, new()
+        {
+            GUIArray newArray = new GUIArray();
+            newArray.Construct<T>(title, array, array.GetType(), layout);
+
+            return newArray;
+        }
+
+        /// <summary>
+        /// Creates a new GUI array with an initially empty array.
+        /// </summary>
+        /// <typeparam name="T">Type of rows that are used to handle GUI for individual list elements.</typeparam>
+        /// <param name="title">Label to display on the list GUI title.</param>
+        /// <param name="arrayType">Type of the array to create GUI for. Must be of <see cref="System.Array"/> type.</param>
+        /// <param name="layout">Layout to which to append the list GUI elements to.</param>
+        public static GUIArray Create<T>(LocString title, Type arrayType, GUILayout layout) where T : GUIListRow, new()
+        {
+            GUIArray newArray = new GUIArray();
+            newArray.Construct<T>(title, null, arrayType, layout);
+
+            return newArray;
+        }
+
+        /// <summary>
+        /// Refreshes contents of all array rows and checks if anything was modified.
+        /// </summary>
+        /// <returns>True if any entry in the array was modified, false otherwise.</returns>
+        public bool Refresh()
+        {
+            bool anythingModified = false;
+
+            for (int i = 0; i < rows.Count; i++)
+                anythingModified |= rows[i].Refresh();
+
+            return anythingModified;
+        }
+
+        /// <summary>
+        /// Destroys the GUI elements.
+        /// </summary>
+        public void Destroy()
+        {
+            if (guiTitleLayout != null)
+            {
+                guiTitleLayout.Destroy();
+                guiTitleLayout = null;
+            }
+
+            if (guiChildLayout != null)
+            {
+                guiChildLayout.Destroy();
+                guiChildLayout = null;
+            }
+
+            for (int i = 0; i < rows.Count; i++)
+                rows[i].Destroy();
+
+            rows.Clear();
+        }
+
+        /// <inheritdoc/>
+        protected override void OnCreateButtonClicked()
+        {
+            list = Array.CreateInstance(listType.GetElementType(), 0);
+
+            OnChanged((Array)list);
+        }
+
+        /// <inheritdoc/>
+        protected override void OnResizeButtonClicked()
+        {
+            int size = guiSizeField.Value;
+
+            Array newArray = Array.CreateInstance(listType.GetElementType(), size);
+
+            int maxSize = MathEx.Min(size, list.Count);
+
+            for (int i = 0; i < maxSize; i++)
+                newArray.SetValue(list[i], i);
+
+            list = newArray;
+
+            OnChanged((Array)list);
+        }
+
+        /// <inheritdoc/>
+        protected override void OnClearButtonClicked()
+        {
+            list = null;
+
+            OnChanged((Array)list);
+        }
+
+        /// <inheritdoc/>
+        protected internal override object GetValue(int seqIndex)
+        {
+            return list[seqIndex];
+        }
+
+        /// <inheritdoc/>
+        protected internal override void SetValue(int seqIndex, object value)
+        {
+            list[seqIndex] = value;
+        }
+
+        /// <inheritdoc/>
+        protected internal override void OnDeleteButtonClicked(int index)
+        {
+            int size = MathEx.Max(0, list.Count - 1);
+            Array newArray = Array.CreateInstance(listType.GetElementType(), size);
+
+            int destIdx = 0;
+            for (int i = 0; i < list.Count; i++)
+            {
+                if (i == index)
+                    continue;
+
+                newArray.SetValue(list[i], destIdx);
+                destIdx++;
+            }
+
+            list = newArray;
+
+            OnChanged((Array)list);
+        }
+
+        /// <inheritdoc/>
+        protected internal override void OnCloneButtonClicked(int index)
+        {
+            int size = list.Count + 1;
+            Array newArray = Array.CreateInstance(listType.GetElementType(), size);
+
+            object clonedEntry = null;
+            for (int i = 0; i < list.Count; i++)
+            {
+                object value = list[i];
+
+                newArray.SetValue(value, i);
+
+                if (i == index)
+                {
+                    if (value == null)
+                        clonedEntry = null;
+                    else
+                    {
+                        ValueType valueType = value as ValueType;
+                        if (valueType != null)
+                            clonedEntry = valueType;
+                        else
+                        {
+                            ICloneable cloneable = value as ICloneable;
+
+                            if (cloneable != null)
+                                clonedEntry = cloneable.Clone();
+                            else
+                                clonedEntry = Activator.CreateInstance(value.GetType());
+                        }
+                    }
+                }
+            }
+
+            newArray.SetValue(clonedEntry, size - 1);
+
+            list = newArray;
+
+            OnChanged((Array)list);
+        }
+
+        /// <inheritdoc/>
+        protected internal override void OnMoveUpButtonClicked(int index)
+        {
+            if ((index - 1) >= 0)
+            {
+                object previousEntry = list[index - 1];
+
+                list[index - 1] = list[index];
+                list[index] = previousEntry;
+            }
+        }
+
+        /// <inheritdoc/>
+        protected internal override void OnMoveDownButtonClicked(int index)
+        {
+            if ((index + 1) < list.Count)
+            {
+                object nextEntry = list[index + 1];
+
+                list[index + 1] = list[index];
+                list[index] = nextEntry;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Contains GUI elements for a single entry in a list.
+    /// </summary>
+    public abstract class GUIListRow
+    {
+        private GUILayoutX rowLayout;
+        private GUIListBase parent;
+
+        protected int seqIndex;
+
+        /// <summary>
+        /// Constructs a new array row object.
+        /// </summary>
+        protected GUIListRow()
+        {
+
+        }
+
+        /// <summary>
+        /// Recreates all row GUI elements.
+        /// </summary>
+        /// <param name="parent">Parent array GUI object that the entry is contained in.</param>
+        /// <param name="parentLayout">Parent layout that row GUI elements will be added to.</param>
+        /// <param name="seqIndex">Sequential index of the array entry.</param>
+        public void Update(GUIListBase parent, GUILayout parentLayout, int seqIndex)
+        {
+            this.parent = parent;
+            this.seqIndex = seqIndex;
+
+            if (rowLayout != null)
+            {
+                rowLayout.Destroy();
+                rowLayout = null;
+            }
+
+            rowLayout = parentLayout.AddLayoutX();
+            GUILayoutY contentLayout = rowLayout.AddLayoutY();
+
+            GUILayoutX titleLayout = CreateGUI(contentLayout);
+
+            if (titleLayout == null)
+            {
+                GUILayoutY buttonCenter = rowLayout.AddLayoutY();
+                buttonCenter.AddFlexibleSpace();
+                titleLayout = buttonCenter.AddLayoutX();
+                buttonCenter.AddFlexibleSpace();
+            }
+
+            GUIButton cloneBtn = new GUIButton("C", GUIOption.FixedWidth(20));
+            GUIButton deleteBtn = new GUIButton("X", GUIOption.FixedWidth(20));
+            GUIButton moveUpBtn = new GUIButton("U", GUIOption.FixedWidth(20));
+            GUIButton moveDownBtn = new GUIButton("D", GUIOption.FixedWidth(20));
+
+            cloneBtn.OnClick += () => parent.OnCloneButtonClicked(seqIndex);
+            deleteBtn.OnClick += () => parent.OnDeleteButtonClicked(seqIndex);
+            moveUpBtn.OnClick += () => parent.OnMoveUpButtonClicked(seqIndex);
+            moveDownBtn.OnClick += () => parent.OnMoveDownButtonClicked(seqIndex);
+
+            titleLayout.AddElement(cloneBtn);
+            titleLayout.AddElement(deleteBtn);
+            titleLayout.AddElement(moveUpBtn);
+            titleLayout.AddElement(moveDownBtn);
+        }
+
+        /// <summary>
+        /// Creates GUI elements specific to type in the array row.
+        /// </summary>
+        /// <param name="layout">Layout to insert the row GUI elements to.</param>
+        /// <returns>An optional title bar layout that the standard array buttons will be appended to.</returns>
+        protected abstract GUILayoutX CreateGUI(GUILayoutY layout);
+        
+        /// <summary>
+        /// Refreshes the GUI for the list row and checks if anything was modified.
+        /// </summary>
+        /// <returns>True if any modifications were made, false otherwise.</returns>
+        internal protected virtual bool Refresh()
+        {
+            return false;
+        }
+
+        /// <summary>
+        /// Gets the value contained in this list row.
+        /// </summary>
+        /// <typeparam name="T">Type of the value. Must match the list's element type.</typeparam>
+        /// <returns>Value in this list row.</returns>
+        protected T GetValue<T>()
+        {
+            return (T)parent.GetValue(seqIndex);
+        }
+
+        /// <summary>
+        /// Sets the value contained in this list row.
+        /// </summary>
+        /// <typeparam name="T">Type of the value. Must match the list's element type.</typeparam>
+        /// <param name="value">Value to set.</param>
+        protected void SetValue<T>(T value)
+        {
+            parent.SetValue(seqIndex, value);
+        }
+
+        /// <summary>
+        /// Destroys all row GUI elements.
+        /// </summary>
+        public void Destroy()
+        {
+            rowLayout.Destroy();
+        }
+    }
+}

+ 6 - 6
MBansheeEditor/ImportOptions.cs

@@ -243,7 +243,7 @@ namespace BansheeEditor
         /// <summary>
         /// Font sizes in points that are to be imported.
         /// </summary>
-        public UInt32[] FontSizes
+        public int[] FontSizes
         {
             get { return Internal_GetFontSizes(mCachedPtr); }
             set { Internal_SetFontSizes(mCachedPtr, value); }
@@ -252,7 +252,7 @@ namespace BansheeEditor
         /// <summary>
         /// Dots per inch resolution to use when rendering the characters into the texture.
         /// </summary>
-        public UInt32 DPI
+        public int DPI
         {
             get { return Internal_GetDPI(mCachedPtr); }
             set { Internal_SetDPI(mCachedPtr, value); }
@@ -280,16 +280,16 @@ namespace BansheeEditor
         private static extern void Internal_CreateInstance(FontImportOptions instance);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern UInt32[] Internal_GetFontSizes(IntPtr thisPtr);
+        private static extern int[] Internal_GetFontSizes(IntPtr thisPtr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetFontSizes(IntPtr thisPtr, UInt32[] value);
+        private static extern void Internal_SetFontSizes(IntPtr thisPtr, int[] value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern UInt32 Internal_GetDPI(IntPtr thisPtr);
+        private static extern int Internal_GetDPI(IntPtr thisPtr);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetDPI(IntPtr thisPtr, UInt32 value);
+        private static extern void Internal_SetDPI(IntPtr thisPtr, int value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_GetAntialiasing(IntPtr thisPtr);

+ 268 - 0
MBansheeEditor/Inspectors/FontInspector.cs

@@ -0,0 +1,268 @@
+using System;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Renders an inspector for the <see cref="Font"/> resource.
+    /// </summary>
+    [CustomInspector(typeof(Font))]
+    internal class FontInspector : Inspector
+    {
+        private bool isInitialized;
+        private GUIArray fontSizes;
+        private GUIArray charRanges;
+        private GUIToggleField antialiasingField;
+        private GUIIntField dpiField;
+        private GUIButton reimportButton;
+
+        private FontImportOptions importOptions;
+
+        /// <summary>
+        /// Initializes required data the first time <see cref="Refresh"/> is called.
+        /// </summary>
+        private void Initialize()
+        {
+            if (referencedObject != null)
+            {
+                importOptions = GetImportOptions();
+
+                RebuildGUI();
+            }
+
+            isInitialized = true;
+        }
+
+        /// <summary>
+        /// Retrieves import options for the texture we're currently inspecting.
+        /// </summary>
+        /// <returns>Font import options object.</returns>
+        private FontImportOptions GetImportOptions()
+        {
+            Font font = referencedObject as Font;
+            FontImportOptions output = null;
+
+            if (font != null)
+            {
+                LibraryEntry texEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(font));
+                if (texEntry != null && texEntry.Type == LibraryEntryType.File)
+                {
+                    FileEntry texFileEntry = (FileEntry)texEntry;
+                    output = texFileEntry.Options as FontImportOptions;
+                }
+            }
+
+            if (output == null)
+            {
+                if (importOptions == null)
+                    output = new FontImportOptions();
+                else
+                    output = importOptions;
+            }
+
+            return output;
+        }
+
+        /// <summary>
+        /// Recreates all the GUI elements used by this inspector.
+        /// </summary>
+        private void RebuildGUI()
+        {
+            layout.Clear();
+
+            fontSizes = GUIArray.Create<FontSizeArrayRow>(new LocEdString("Font sizes"), importOptions.FontSizes, layout);
+            fontSizes.OnChanged += x =>
+            {
+                int[] newFontSizes = x as int[];
+                importOptions.FontSizes = newFontSizes;
+
+                RebuildGUI();
+            };
+
+            charRanges = GUIArray.Create<CharRangeArrayRow>(new LocEdString("Character ranges"), importOptions.CharRanges, layout);
+            charRanges.OnChanged += x =>
+            {
+                CharRange[] newRanges = x as CharRange[];
+                importOptions.CharRanges = newRanges;
+
+                RebuildGUI();
+            };
+
+            antialiasingField = new GUIToggleField(new LocEdString("Antialiasing"));
+            dpiField = new GUIIntField(new LocEdString("DPI"));
+
+            reimportButton = new GUIButton(new LocEdString("Reimport"));
+            reimportButton.OnClick += TriggerReimport;
+
+            layout.AddElement(antialiasingField);
+            layout.AddElement(dpiField);
+            layout.AddSpace(10);
+
+            GUILayout reimportButtonLayout = layout.AddLayoutX();
+            reimportButtonLayout.AddFlexibleSpace();
+            reimportButtonLayout.AddElement(reimportButton);
+        }
+
+        /// <summary>
+        /// Reimports the texture resource according to the currently set import options.
+        /// </summary>
+        private void TriggerReimport()
+        {
+            Texture2D texture = (Texture2D)referencedObject;
+            string resourcePath = ProjectLibrary.GetPath(texture);
+
+            ProjectLibrary.Reimport(resourcePath, importOptions, true);
+        }
+
+        /// <inheritdoc/>
+        internal override bool Refresh()
+        {
+            if (!isInitialized)
+            {
+                Initialize();
+                isInitialized = true;
+            }
+
+            FontImportOptions newImportOptions = GetImportOptions();
+
+            bool rebuildGUI = false;
+
+            int[] newFontSizes = newImportOptions.FontSizes;
+            if (newFontSizes == null)
+                rebuildGUI |= fontSizes.Array != null;
+            else
+            {
+                if (fontSizes.Array == null)
+                    rebuildGUI = true;
+                else
+                    rebuildGUI |= newFontSizes.Length != fontSizes.Array.GetLength(0);
+            }
+
+            CharRange[] newCharRanges = newImportOptions.CharRanges;
+            if (newCharRanges == null)
+                rebuildGUI |= charRanges.Array != null;
+            else
+            {
+                if (charRanges.Array == null)
+                    rebuildGUI = true;
+                else
+                    rebuildGUI |= newCharRanges.Length != charRanges.Array.GetLength(0);
+            }
+
+            if (rebuildGUI)
+                RebuildGUI();
+
+            bool anythingModified = fontSizes.Refresh();
+            anythingModified |= charRanges.Refresh();
+
+            if (antialiasingField.Value != newImportOptions.Antialiasing)
+            {
+                antialiasingField.Value = newImportOptions.Antialiasing;
+                anythingModified = true;
+            }
+
+            if (dpiField.Value != newImportOptions.DPI)
+            {
+                dpiField.Value = newImportOptions.DPI;
+                anythingModified = true;
+            }
+
+            if (anythingModified)
+                importOptions = newImportOptions;
+
+            return anythingModified;
+        }
+
+        /// <summary>
+        /// Row element used for displaying GUI for font size array elements.
+        /// </summary>
+        public class FontSizeArrayRow : GUIListRow
+        {
+            private GUIIntField sizeField;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateGUI(GUILayoutY layout)
+            {
+                GUILayoutX titleLayout = layout.AddLayoutX();
+                sizeField = new GUIIntField(new LocEdString(seqIndex + ". "));
+                titleLayout.AddElement(sizeField);
+
+                sizeField.OnChanged += SetValue;
+
+                return titleLayout;
+            }
+
+            /// <inheritdoc/>
+            internal protected override bool Refresh()
+            {
+                int newValue = GetValue<int>();
+                if (sizeField.Value != newValue)
+                {
+                    sizeField.Value = newValue;
+                    return true;
+                }
+
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Row element used for displaying GUI for character range array elements.
+        /// </summary>
+        public class CharRangeArrayRow : GUIListRow
+        {
+            private GUIIntField rangeStartField;
+            private GUIIntField rangeEndField;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateGUI(GUILayoutY layout)
+            {
+                GUILayoutX titleLayout = layout.AddLayoutX();
+
+                rangeStartField = new GUIIntField(new LocEdString(seqIndex + ". Start"));
+                rangeEndField = new GUIIntField(new LocEdString("End"));
+
+                titleLayout.AddElement(rangeStartField);
+                titleLayout.AddElement(rangeEndField);
+
+                rangeStartField.OnChanged += x =>
+                {
+                    CharRange range = GetValue<CharRange>();
+                    range.start = x;
+                    SetValue(range);
+                };
+
+                rangeEndField.OnChanged += x =>
+                {
+                    CharRange range = GetValue<CharRange>();
+                    range.end = x;
+                    SetValue(range);
+                };
+
+                return titleLayout;
+            }
+
+            /// <inheritdoc/>
+            internal protected override bool Refresh()
+            {
+                bool anythingModified = false;
+
+                CharRange newValue = GetValue<CharRange>();
+                if (rangeStartField.Value != newValue.start)
+                {
+                    rangeStartField.Value = newValue.start;
+                    anythingModified = true;
+                }
+
+                if (rangeEndField.Value != newValue.end)
+                {
+                    rangeEndField.Value = newValue.end;
+                    anythingModified = true;
+                }
+
+                return anythingModified;
+            }
+        }
+    }
+}

+ 31 - 0
MBansheeEditor/Inspectors/PrefabInspector.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Renders an inspector for the <see cref="Prefab"/> resource.
+    /// </summary>
+    [CustomInspector(typeof(Prefab))]
+    internal class PrefabInspector : Inspector
+    {
+        private bool isInitialized;
+
+        /// <inheritdoc/>
+        internal override bool Refresh()
+        {
+            Prefab prefab = referencedObject as Prefab;
+            if (prefab == null)
+                return false;
+
+            if (!isInitialized)
+            {
+                // No GUI for prefab resource
+
+                isInitialized = true;
+            }
+
+            return false;
+        }
+    }
+}

+ 3 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -48,6 +48,7 @@
     <Compile Include="DropDownWindow.cs" />
     <Compile Include="FolderMonitor.cs" />
     <Compile Include="GameWindow.cs" />
+    <Compile Include="GUI\GUIArray.cs" />
     <Compile Include="GUI\GUIEnumField.cs" />
     <Compile Include="GUI\GUIListBoxField.cs" />
     <Compile Include="GUI\GUISceneTreeView.cs" />
@@ -55,10 +56,12 @@
     <Compile Include="GUI\TextureField.cs" />
     <Compile Include="HierarchyWindow.cs" />
     <Compile Include="Inspectors\CameraInspector.cs" />
+    <Compile Include="Inspectors\FontInspector.cs" />
     <Compile Include="Inspectors\LightInspector.cs" />
     <Compile Include="Inspectors\MaterialInspector.cs" />
     <Compile Include="Inspectors\MeshInspector.cs" />
     <Compile Include="Inspectors\PlainTextInspector.cs" />
+    <Compile Include="Inspectors\PrefabInspector.cs" />
     <Compile Include="Inspectors\ScriptCodeInspector.cs" />
     <Compile Include="Inspectors\Texture2DInspector.cs" />
     <Compile Include="Inspector\InspectorUtility.cs" />

+ 2 - 2
MBansheeEngine/Font.cs

@@ -146,8 +146,8 @@ namespace BansheeEngine
     [StructLayout(LayoutKind.Sequential), SerializeObject]
     public struct CharRange
     {
-        public UInt32 start;
-        public UInt32 end;
+        public int start;
+        public int end;
     }
 
     /// <summary>

+ 11 - 0
MBansheeEngine/GUI/GUILayout.cs

@@ -222,6 +222,14 @@ namespace BansheeEngine
             return space;
         }
 
+        /// <summary>
+        /// Destroys all children of this layout.
+        /// </summary>
+        public void Clear()
+        {
+            Internal_Clear(mCachedPtr);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         protected static extern void Internal_CreateInstanceYFromScrollArea(GUILayout instance, GUIScrollArea parentArea);
 
@@ -246,5 +254,8 @@ namespace BansheeEngine
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         protected static extern GUIElement Internal_GetChild(IntPtr instance, int index);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        protected static extern void Internal_Clear(IntPtr instance);
     }
 }

+ 1 - 0
SBansheeEngine/Include/BsScriptGUILayout.h

@@ -80,6 +80,7 @@ namespace BansheeEngine
 		static void internal_insertElement(ScriptGUILayout* instance, UINT32 index, ScriptGUIElementBaseTBase* element);
 		static UINT32 internal_getChildCount(ScriptGUILayout* instance);
 		static MonoObject* internal_getChild(ScriptGUILayout* instance, UINT32 index);
+		static void internal_clear(ScriptGUILayout* instance);
 
 		static void internal_createInstanceYFromScrollArea(MonoObject* instance, MonoObject* parentScrollArea);
 	};

+ 17 - 0
SBansheeEngine/Source/BsScriptGUILayout.cpp

@@ -28,6 +28,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_InsertElement", &ScriptGUILayout::internal_insertElement);
 		metaData.scriptClass->addInternalCall("Internal_GetChildCount", &ScriptGUILayout::internal_getChildCount);
 		metaData.scriptClass->addInternalCall("Internal_GetChild", &ScriptGUILayout::internal_getChild);
+		metaData.scriptClass->addInternalCall("Internal_Clear", &ScriptGUILayout::internal_clear);
 	}
 
 	void ScriptGUILayout::destroy()
@@ -183,6 +184,22 @@ namespace BansheeEngine
 		return instance->mChildren[index].element->getManagedInstance();
 	}
 
+	void ScriptGUILayout::internal_clear(ScriptGUILayout* instance)
+	{
+		if (instance->isDestroyed())
+			return;
+
+		for (auto& child : instance->mChildren)
+		{
+			mono_gchandle_free(child.gcHandle);
+
+			child.element->setParent(nullptr);
+			child.element->destroy();
+		}
+
+		instance->mChildren.clear();
+	}
+
 	ScriptGUIPanel::ScriptGUIPanel(MonoObject* instance)
 		:ScriptObject(instance)
 	{ }

+ 1 - 0
TODO.txt

@@ -59,6 +59,7 @@ Ribek use:
  - Component inspector for Renderable
  - Resource inspectors for: Font, Shader, Script Code, Plain Text, Sprite Texture, GUISkin, StringTable, Prefab (just something basic for now)
  - Test release mode
+ - I'm missing array & object management icons (Create, Clone, Delete, Resize, Move up, Move down)
 
 Other polish:
  - Add menu items: