/// <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}"; InspectableField inspectableField = null; if (fieldCreateCallback != null) { inspectableField = fieldCreateCallback(path, new InspectableFieldLayout(parentLayout), currentIndex, depth); } 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> /// Creates inspectable fields all the fields/properties of the specified object. /// </summary> /// <param name="obj">Object whose fields the GUI will be drawn for.</param> /// <param name="context">Context shared by all inspectable fields created by the same parent.</param> /// <param name="path">Full path to the field this provided object was retrieved from.</param> /// <param name="depth"> /// Determines how deep within the inspector nesting hierarchy is this objects. Some fields may contain other /// fields, in which case you should increase this value by one. /// </param> /// <param name="layout">Parent layout that all the field GUI elements will be added to.</param> /// <param name="overrideCallback"> /// Optional callback that allows you to override the look of individual fields in the object. If non-null the /// callback will be called with information about every field in the provided object. If the callback returns /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type /// will be used. /// </param> public static List <InspectableField> CreateFields(SerializableObject obj, InspectableContext context, string path, int depth, GUILayoutY layout, FieldOverrideCallback overrideCallback = null) { // Retrieve fields and sort by order SerializableField[] fields = obj.Fields; Array.Sort(fields, (x, y) => { int orderX = x.Flags.HasFlag(SerializableFieldAttributes.Order) ? x.Style.Order : 0; int orderY = y.Flags.HasFlag(SerializableFieldAttributes.Order) ? y.Style.Order : 0; return(orderX.CompareTo(orderY)); }); // Generate per-field GUI while grouping by category int rootIndex = 0; int categoryIndex = 0; string categoryName = null; InspectableCategory category = null; List <InspectableField> inspectableFields = new List <InspectableField>(); foreach (var field in fields) { if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable)) { continue; } if (field.Flags.HasFlag(SerializableFieldAttributes.Category)) { string newCategory = field.Style.CategoryName; if (!string.IsNullOrEmpty(newCategory) && categoryName != newCategory) { string categoryPath = path + "/[" + newCategory + "]"; category = new InspectableCategory(context, newCategory, categoryPath, depth, new InspectableFieldLayout(layout)); category.Initialize(rootIndex); category.Refresh(rootIndex); rootIndex += category.GetNumLayoutElements(); inspectableFields.Add(category); categoryName = newCategory; categoryIndex = 0; } else { categoryName = null; category = null; } } int currentIndex; GUILayoutY parentLayout; if (category != null) { currentIndex = categoryIndex; parentLayout = category.ChildLayout; } else { currentIndex = rootIndex; parentLayout = layout; } string fieldName = GetReadableIdentifierName(field.Name); string childPath = string.IsNullOrEmpty(path) ? fieldName : path + "/" + fieldName; InspectableField inspectableField = null; if (overrideCallback != null) { inspectableField = overrideCallback(field, context, path, new InspectableFieldLayout(parentLayout), currentIndex, depth); } if (inspectableField == null) { inspectableField = CreateField(context, fieldName, childPath, currentIndex, depth, new InspectableFieldLayout(parentLayout), field.GetProperty(), InspectableFieldStyle.Create(field)); } if (category != null) { category.AddChild(inspectableField); } else { inspectableFields.Add(inspectableField); } currentIndex += inspectableField.GetNumLayoutElements(); if (category != null) { categoryIndex = currentIndex; } else { rootIndex = currentIndex; } } return(inspectableFields); }
/// <summary> /// Creates inspectable fields all the fields/properties of the specified object. /// </summary> /// <param name="obj">Object whose fields the GUI will be drawn for.</param> /// <param name="parent">Parent Inspector to draw in.</param> /// <param name="path">Full path to the field this provided object was retrieved from.</param> /// <param name="depth"> /// Determines how deep within the inspector nesting hierarchy is this objects. Some fields may contain other /// fields, in which case you should increase this value by one. /// </param> /// <param name="layout">Parent layout that all the field GUI elements will be added to.</param> /// <param name="overrideCallback"> /// Optional callback that allows you to override the look of individual fields in the object. If non-null the /// callback will be called with information about every field in the provided object. If the callback returns /// non-null that inspectable field will be used for drawing the GUI, otherwise the default inspector field type /// will be used. /// </param> public static List <InspectableField> CreateFields(SerializableObject obj, Inspector parent, string path, int depth, GUILayoutY layout, FieldOverrideCallback overrideCallback = null) { // Retrieve fields and sort by order SerializableField[] fields = obj.Fields; Array.Sort(fields, (x, y) => { int orderX = x.Flags.HasFlag(SerializableFieldAttributes.Order) ? x.Style.Order : 0; int orderY = y.Flags.HasFlag(SerializableFieldAttributes.Order) ? y.Style.Order : 0; return(orderX.CompareTo(orderY)); }); // Generate per-field GUI while grouping by category Dictionary <string, Tuple <int, GUILayoutY> > categories = new Dictionary <string, Tuple <int, GUILayoutY> >(); int rootIndex = 0; List <InspectableField> inspectableFields = new List <InspectableField>(); foreach (var field in fields) { if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable)) { continue; } string category = null; if (field.Flags.HasFlag(SerializableFieldAttributes.Category)) { category = field.Style.CategoryName; } Tuple <int, GUILayoutY> categoryInfo = null; if (!string.IsNullOrEmpty(category)) { if (!categories.TryGetValue(category, out categoryInfo)) { InspectableFieldLayout fieldLayout = new InspectableFieldLayout(layout); GUILayoutY categoryRootLayout = fieldLayout.AddLayoutY(rootIndex); GUILayoutX guiTitleLayout = categoryRootLayout.AddLayoutX(); bool isExpanded = parent.Persistent.GetBool(path + "/[" + category + "]_Expanded"); GUIToggle guiFoldout = new GUIToggle(category, EditorStyles.Foldout); guiFoldout.Value = isExpanded; guiFoldout.AcceptsKeyFocus = false; guiFoldout.OnToggled += x => { parent.Persistent.SetBool(path + "/[" + category + "]_Expanded", x); }; guiTitleLayout.AddElement(guiFoldout); GUILayoutX categoryContentLayout = categoryRootLayout.AddLayoutX(); categoryContentLayout.AddSpace(IndentAmount); GUIPanel guiContentPanel = categoryContentLayout.AddPanel(); GUILayoutX guiIndentLayoutX = guiContentPanel.AddLayoutX(); guiIndentLayoutX.AddSpace(IndentAmount); GUILayoutY guiIndentLayoutY = guiIndentLayoutX.AddLayoutY(); guiIndentLayoutY.AddSpace(IndentAmount); GUILayoutY categoryLayout = guiIndentLayoutY.AddLayoutY(); guiIndentLayoutY.AddSpace(IndentAmount); guiIndentLayoutX.AddSpace(IndentAmount); categoryContentLayout.AddSpace(IndentAmount); short backgroundDepth = (short)(Inspector.START_BACKGROUND_DEPTH - depth - 1); string bgPanelStyle = depth % 2 == 0 ? EditorStylesInternal.InspectorContentBgAlternate : EditorStylesInternal.InspectorContentBg; GUIPanel backgroundPanel = guiContentPanel.AddPanel(backgroundDepth); GUITexture inspectorContentBg = new GUITexture(null, bgPanelStyle); backgroundPanel.AddElement(inspectorContentBg); categories[category] = new Tuple <int, GUILayoutY>(0, categoryLayout); rootIndex++; } } int currentIndex; GUILayoutY parentLayout; if (categoryInfo != null) { currentIndex = categoryInfo.Item1; parentLayout = categoryInfo.Item2; } else { currentIndex = rootIndex; parentLayout = layout; } string fieldName = field.Name; string childPath = string.IsNullOrEmpty(path) ? fieldName : path + "/" + fieldName; InspectableField inspectableField = null; if (overrideCallback != null) { inspectableField = overrideCallback(field, parent, path, new InspectableFieldLayout(parentLayout), currentIndex, depth); } if (inspectableField == null) { inspectableField = CreateField(parent, fieldName, childPath, currentIndex, depth, new InspectableFieldLayout(parentLayout), field.GetProperty(), InspectableFieldStyle.Create(field)); } inspectableFields.Add(inspectableField); currentIndex += inspectableField.GetNumLayoutElements(); if (categoryInfo != null) { categories[category] = new Tuple <int, GUILayoutY>(currentIndex, parentLayout); } else { rootIndex = currentIndex; } } return(inspectableFields); }