/// <inheritdoc/> protected internal override void Initialize() { if (InspectedObject == null) { LoadResource(); } if (InspectedObject != null) { int currentIndex = 0; SerializableObject serializableObject = new SerializableObject(InspectedObject.GetType(), InspectedObject); foreach (var field in serializableObject.Fields) { if (!field.Inspectable) { continue; } string path = field.Name; InspectableField inspectableField = InspectableField.CreateInspectable(this, field.Name, path, currentIndex, 0, new InspectableFieldLayout(Layout), field.GetProperty(), InspectableFieldStyle.Create(field)); inspectableFields.Add(inspectableField); isEmpty = false; currentIndex += inspectableField.GetNumLayoutElements(); } base.SetVisible(!isEmpty); } }
/// <inheritdoc/> protected override void CreateValueGUI(GUILayoutY layout) { InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent; SerializableProperty property = GetValue <SerializableProperty>(); string entryPath = dictParent.Path + "Value[" + RowIdx + "]"; fieldValue = CreateInspectable(dictParent.Inspector, "Value", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property); }
/// <inheritdoc/> protected override GUILayoutX CreateGUI(GUILayoutY layout) { InspectableListGUI listParent = (InspectableListGUI)parent; SerializableProperty property = GetValue <SerializableProperty>(); string entryPath = listParent.Path + "[" + SeqIndex + "]"; field = CreateInspectable(listParent.Inspector, SeqIndex + ".", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property, new InspectableFieldStyleInfo()); return(field.GetTitleLayout()); }
/// <inheritdoc/> protected override GUILayoutX CreateKeyGUI(GUILayoutY layout) { keyLayout = layout; InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent; SerializableProperty property = GetKey <SerializableProperty>(); string entryPath = dictParent.Path + "Key[" + RowIdx + "]"; fieldKey = CreateInspectable(dictParent.Inspector, "Key", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property); return(fieldKey.GetTitleLayout()); }
/// <inheritdoc/> protected override GUILayoutX CreateGUI(GUILayoutY layout) { InspectableArrayGUI arrayParent = (InspectableArrayGUI)parent; SerializableProperty property = GetValue <SerializableProperty>(); InspectableFieldStyleInfo styleInfo = arrayParent.Style.Clone(); styleInfo.StyleFlags &= ~InspectableFieldStyleFlags.NativeWrapper; string entryPath = arrayParent.Path + "[" + SeqIndex + "]"; field = CreateInspectable(arrayParent.Inspector, SeqIndex + ".", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property, styleInfo); return(field.GetTitleLayout()); }
/// <inheritdoc/> protected internal override void Initialize() { if (InspectedObject == null) { LoadResource(); } if (InspectedObject != null) { SerializableObject serializableObject = new SerializableObject(InspectedObject.GetType(), InspectedObject); inspectableFields = InspectableField.CreateFields(serializableObject, this, "", 0, Layout); isEmpty = inspectableFields.Count > 0; base.SetVisible(!isEmpty); } }
/// <summary> /// Rebuilds the GUI object header if needed. /// </summary> /// <param name="layoutIndex">Index at which to insert the GUI elements.</param> protected void BuildGUI(int layoutIndex) { Action BuildEmptyGUI = () => { guiInternalTitleLayout = guiTitleLayout.InsertLayoutX(0); guiInternalTitleLayout.AddElement(new GUILabel(title)); guiInternalTitleLayout.AddElement(new GUILabel("Empty", GUIOption.FixedWidth(100))); if (!property.IsValueType) { GUIContent createIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Create), new LocEdString("Create")); guiCreateBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30)); guiCreateBtn.OnClick += OnCreateButtonClicked; guiInternalTitleLayout.AddElement(guiCreateBtn); } }; Action BuildFilledGUI = () => { guiInternalTitleLayout = guiTitleLayout.InsertLayoutX(0); GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout); guiFoldout.Value = isExpanded; guiFoldout.AcceptsKeyFocus = false; guiFoldout.OnToggled += OnFoldoutToggled; guiInternalTitleLayout.AddElement(guiFoldout); if (!style.StyleFlags.HasFlag(InspectableFieldStyleFlags.NotNull)) { GUIContent clearIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Clear), new LocEdString("Clear")); GUIButton clearBtn = new GUIButton(clearIcon, GUIOption.FixedWidth(20)); clearBtn.OnClick += OnClearButtonClicked; guiInternalTitleLayout.AddElement(clearBtn); } if (isExpanded) { SerializableObject serializableObject = property.GetObject(); SerializableField[] fields = serializableObject.Fields; if (fields.Length > 0) { guiChildLayout = guiLayout.AddLayoutX(); guiChildLayout.AddSpace(IndentAmount); 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); 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); int currentIndex = 0; foreach (var field in fields) { if (!field.Flags.HasFlag(SerializableFieldAttributes.Inspectable)) { continue; } string childPath = path + "/" + field.Name; InspectableField inspectable = CreateInspectable(parent, field.Name, childPath, currentIndex, depth + 1, new InspectableFieldLayout(guiContentLayout), field.GetProperty(), InspectableFieldStyle.Create(field)); children.Add(inspectable); currentIndex += inspectable.GetNumLayoutElements(); } } } else { guiChildLayout = null; } }; if (state == State.None) { if (propertyValue != null) { BuildFilledGUI(); state = State.Filled; } else { BuildEmptyGUI(); state = State.Empty; } } else if (state == State.Empty) { if (propertyValue != null) { guiInternalTitleLayout.Destroy(); guiCreateBtn = null; BuildFilledGUI(); state = State.Filled; } } else if (state == State.Filled) { foreach (var child in children) { child.Destroy(); } children.Clear(); guiInternalTitleLayout.Destroy(); guiCreateBtn = null; if (guiChildLayout != null) { guiChildLayout.Destroy(); guiChildLayout = null; } if (propertyValue == null) { BuildEmptyGUI(); state = State.Empty; } else { BuildFilledGUI(); } } }
/// <inheritdoc/> protected override void CreateValueGUI(GUILayoutY layout) { InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent; SerializableProperty property = GetValue<SerializableProperty>(); string entryPath = dictParent.Path + "Value[" + RowIdx + "]"; fieldValue = CreateInspectable(dictParent.Inspector, "Value", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property); }
/// <inheritdoc/> protected override GUILayoutX CreateKeyGUI(GUILayoutY layout) { keyLayout = layout; InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent; SerializableProperty property = GetKey<SerializableProperty>(); string entryPath = dictParent.Path + "Key[" + RowIdx + "]"; fieldKey = CreateInspectable(dictParent.Inspector, "Key", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property); return fieldKey.GetTitleLayout(); }
/// <summary> /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations /// (like ones for primitives like int or bool), or a user defined implementation defined with a /// <see cref="CustomInspector"/> attribute. /// </summary> /// <param name="parent">Parent Inspector this field belongs to.</param> /// <param name="title">Name of the property, or some other value to set as the title.</param> /// <param name="path">Full path to this property (includes name of this property and all parent properties).</param> /// <param name="layoutIndex">Index into the parent layout at which to insert the GUI elements for the field .</param> /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. 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 elements will be added to.</param> /// <param name="property">Serializable property referencing the array whose contents to display.</param> /// <param name="style">Information that can be used for customizing field rendering and behaviour.</param> /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property /// of the provided type.</returns> public static InspectableField CreateInspectable(Inspector parent, string title, string path, int layoutIndex, int depth, InspectableFieldLayout layout, SerializableProperty property, InspectableFieldStyleInfo style = null) { InspectableField field = null; Type type = property.InternalType; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(RRef <>)) { type = type.GenericTypeArguments[0]; } Type customInspectable = InspectorUtility.GetCustomInspectable(type); if (customInspectable != null) { field = (InspectableField)Activator.CreateInstance(customInspectable, depth, title, property); } else { switch (property.Type) { case SerializableProperty.FieldType.Int: if (style != null && style.StyleFlags.HasFlag(InspectableFieldStyleFlags.UseLayerMask)) { field = new InspectableLayerMask(parent, title, path, depth, layout, property); } else { if (style?.RangeStyle == null || !style.RangeStyle.Slider) { field = new InspectableInt(parent, title, path, depth, layout, property, style); } else { field = new InspectableRangedInt(parent, title, path, depth, layout, property, style); } } break; case SerializableProperty.FieldType.Float: if (style?.RangeStyle == null || !style.RangeStyle.Slider) { field = new InspectableFloat(parent, title, path, depth, layout, property, style); } else { field = new InspectableRangedFloat(parent, title, path, depth, layout, property, style); } break; case SerializableProperty.FieldType.Bool: field = new InspectableBool(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Color: field = new InspectableColor(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.ColorGradient: field = new InspectableColorGradient(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Curve: field = new InspectableCurve(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.FloatDistribution: field = new InspectableFloatDistribution(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector2Distribution: field = new InspectableVector2Distribution(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector3Distribution: field = new InspectableVector3Distribution(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.ColorDistribution: field = new InspectableColorDistribution(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.String: field = new InspectableString(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector2: field = new InspectableVector2(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector3: field = new InspectableVector3(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector4: field = new InspectableVector4(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Quaternion: if (style != null && style.StyleFlags.HasFlag(InspectableFieldStyleFlags.AsQuaternion)) { field = new InspectableQuaternion(parent, title, path, depth, layout, property); } else { field = new InspectableEuler(parent, title, path, depth, layout, property); } break; case SerializableProperty.FieldType.Resource: field = new InspectableResource(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.RRef: field = new InspectableRRef(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.GameObjectRef: field = new InspectableGameObjectRef(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Object: field = new InspectableObject(parent, title, path, depth, layout, property, style); break; case SerializableProperty.FieldType.Array: field = new InspectableArray(parent, title, path, depth, layout, property, style); break; case SerializableProperty.FieldType.List: field = new InspectableList(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Dictionary: field = new InspectableDictionary(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Enum: field = new InspectableEnum(parent, title, path, depth, layout, property); break; } } if (field == null) { throw new Exception("No inspector exists for the provided field type."); } field.Initialize(layoutIndex); field.Refresh(layoutIndex); return(field); }
/// <summary> /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations /// (like ones for primitives like int or bool), or a user defined implementation defined with a /// <see cref="CustomInspector"/> attribute. /// </summary> /// <param name="parent">Parent Inspector this field belongs to.</param> /// <param name="title">Name of the property, or some other value to set as the title.</param> /// <param name="path">Full path to this property (includes name of this property and all parent properties).</param> /// <param name="layoutIndex">Index into the parent layout at which to insert the GUI elements for the field .</param> /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. 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 elements will be added to.</param> /// <param name="property">Serializable property referencing the array whose contents to display.</param> /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property /// of the provided type.</returns> public static InspectableField CreateInspectable(Inspector parent, string title, string path, int layoutIndex, int depth, InspectableFieldLayout layout, SerializableProperty property) { InspectableField field = null; Type customInspectable = InspectorUtility.GetCustomInspectable(property.InternalType); if (customInspectable != null) { field = (InspectableField)Activator.CreateInstance(customInspectable, depth, title, property); } else { switch (property.Type) { case SerializableProperty.FieldType.Int: field = new InspectableInt(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Float: field = new InspectableFloat(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Bool: field = new InspectableBool(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Color: field = new InspectableColor(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.String: field = new InspectableString(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector2: field = new InspectableVector2(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector3: field = new InspectableVector3(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Vector4: field = new InspectableVector4(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.ResourceRef: field = new InspectableResourceRef(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.GameObjectRef: field = new InspectableGameObjectRef(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Object: field = new InspectableObject(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Array: field = new InspectableArray(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.List: field = new InspectableList(parent, title, path, depth, layout, property); break; case SerializableProperty.FieldType.Dictionary: field = new InspectableDictionary(parent, title, path, depth, layout, property); break; } } if (field == null) { throw new Exception("No inspector exists for the provided field type."); } field.Initialize(layoutIndex); field.Refresh(layoutIndex); return(field); }
/// <inheritdoc/> protected override GUILayoutX CreateGUI(GUILayoutY layout) { InspectableListGUI listParent = (InspectableListGUI)parent; SerializableProperty property = GetValue<SerializableProperty>(); string entryPath = listParent.Path + "[" + SeqIndex + "]"; field = CreateInspectable(listParent.Inspector, SeqIndex + ".", entryPath, 0, Depth + 1, new InspectableFieldLayout(layout), property); return field.GetTitleLayout(); }
/// <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); }