+ public class ColorPaletteExamples : MonoBehaviour
+ {
+ [ColorPalette]
+ public Color ColorOptions;
+
+ [ColorPalette("Underwater")]
+ public Color UnderwaterColor;
+
+ [ColorPalette("Fall"), HideLabel]
+ public Color WideColorPalette;
+
+ [ColorPalette("My Palette")]
+ public Color MyColor;
+
+ [ColorPalette("Clovers")]
+ public Color[] ColorArray;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [CustomContextMenu("My custom option", "MyAction")]
+ public Vector3 MyVector;
+
+ private void MyAction()
+ {
+ MyVector = Random.onUnitSphere;
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [DetailedInfoBox("This is a message", "Here is some more details about that message")]
+ public int MyInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [DisableContextMenu]
+ public Vector3 MyVector;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ public bool DisableProperty;
+
+ [DisableIf("DisableProperty")]
+ public int MyInt;
+
+ public SomeEnum SomeEnumField;
+
+ [DisableIf("SomeEnumField", SomeEnum.SomeEnumMember)]
+ public string SomeString;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [EnableIf("MyDisableFunction")]
+ public int MyInt;
+
+ private bool MyDisableFunction()
+ {
+ // ...
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [DisableInEditorMode]
+ public int MyInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [DisableInPlayMode]
+ public int MyInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [DisplayAsString]
+ public string MyInt = 5;
+
+ // You can combine with to display a message in the inspector.
+ [DisplayAsString, HideLabel]
+ public string MyMessage = "This string will be displayed as text in the inspector";
+
+ [DisplayAsString(false)]
+ public string InlineMessage = "This string is very long, but has been configured to not overflow.";
+ }
+
+
+ [DontApplyToListElements]
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
+ public sealed class VisibleIfAttribute : Attribute
+ {
+ public string MemberName { get; private set; }
+
+ public VisibleIfAttribute(string memberName)
+ {
+ this.MemberName = memberName;
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ public bool EnableProperty;
+
+ [EnableIf("EnableProperty")]
+ public int MyInt;
+
+ public SomeEnum SomeEnumField;
+
+ [EnableIf("SomeEnumField", SomeEnum.SomeEnumMember)]
+ public string SomeString;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [EnableIf("MyEnableFunction")]
+ public int MyInt;
+
+ private bool MyEnableFunction()
+ {
+ // ...
+ }
+ }
+
+
+ public class MyComponent : MonoBehvaiour
+ {
+ [EnumToggleButtons]
+ public MyBitmaskEnum MyBitmaskEnum;
+
+ [EnumToggleButtons]
+ public MyEnum MyEnum;
+ }
+
+ [Flags]
+ public enum MyBitmaskEnum
+ {
+ A = 1 << 1, // 1
+ B = 1 << 2, // 2
+ C = 1 << 3, // 4
+ ALL = A | B | C
+ }
+
+ public enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+
+
- public class FilePathExamples : MonoBehaviour
- {
- // By default, FilePath provides a path relative to the Unity project.
- [FilePath]
- public string UnityProjectPath;
+
+ FilePath is used on string properties, and provides an interface for file paths.
+
+
+ The following example demonstrates how FilePath is used.
+
+ public class FilePathExamples : MonoBehaviour
+ {
+ // By default, FilePath provides a path relative to the Unity project.
+ [FilePath]
+ public string UnityProjectPath;
- // It is possible to provide custom parent path. Parent paths can be relative to the Unity project, or absolute.
- [FilePath(ParentFolder = "Assets/Plugins/Sirenix")]
- public string RelativeToParentPath;
+ // It is possible to provide custom parent path. Parent paths can be relative to the Unity project, or absolute.
+ [FilePath(ParentFolder = "Assets/Plugins/Sirenix")]
+ public string RelativeToParentPath;
- // Using parent path, FilePath can also provide a path relative to a resources folder.
- [FilePath(ParentFolder = "Assets/Resources")]
- public string ResourcePath;
+ // Using parent path, FilePath can also provide a path relative to a resources folder.
+ [FilePath(ParentFolder = "Assets/Resources")]
+ public string ResourcePath;
- // Provide a comma seperated list of allowed extensions. Dots are optional.
- [FilePath(Extensions = "cs")]
- public string ScriptFiles;
+ // Provide a comma seperated list of allowed extensions. Dots are optional.
+ [FilePath(Extensions = "cs")]
+ public string ScriptFiles;
- // By setting AbsolutePath to true, the FilePath will provide an absolute path instead.
- [FilePath(AbsolutePath = true)]
- [BoxGroup("Conditions")]
- public string AbsolutePath;
+ // By setting AbsolutePath to true, the FilePath will provide an absolute path instead.
+ [FilePath(AbsolutePath = true)]
+ [BoxGroup("Conditions")]
+ public string AbsolutePath;
- // FilePath can also be configured to show an error, if the provided path is invalid.
- [FilePath(RequireValidPath = true)]
- public string ValidPath;
+ // FilePath can also be configured to show an error, if the provided path is invalid.
+ [FilePath(RequireValidPath = true)]
+ public string ValidPath;
- // By default, FilePath will enforce the use of forward slashes. It can also be configured to use backslashes instead.
- [FilePath(UseBackslashes = true)]
- public string Backslashes;
+ // By default, FilePath will enforce the use of forward slashes. It can also be configured to use backslashes instead.
+ [FilePath(UseBackslashes = true)]
+ public string Backslashes;
- // FilePath also supports member references with the $ symbol.
- [FilePath(ParentFolder = "$DynamicParent", Extensions = "$DynamicExtensions")]
- public string DynamicFilePath;
+ // FilePath also supports member references with the $ symbol.
+ [FilePath(ParentFolder = "$DynamicParent", Extensions = "$DynamicExtensions")]
+ public string DynamicFilePath;
- public string DynamicParent = "Assets/Plugin/Sirenix";
+ public string DynamicParent = "Assets/Plugin/Sirenix";
- public string DynamicExtensions = "cs, unity, jpg";
- }
-
-
-
-
+ public string DynamicExtensions = "cs, unity, jpg";
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [FoldoutGroup("MyGroup")]
+ public int A;
+
+ [FoldoutGroup("MyGroup")]
+ public int B;
+
+ [FoldoutGroup("MyGroup")]
+ public int C;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [FoldoutGroup("First")]
+ public int A;
+
+ [FoldoutGroup("First")]
+ public int B;
+
+ [FoldoutGroup("Second")]
+ public int C;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [GUIColor(1f, 0f, 0f)]
+ public int A;
+
+ [GUIColor(1f, 0.5f, 0f, 0.2f)]
+ public int B;
+
+ [GUIColor("GetColor")]
+ public int C;
+
+ private Color GetColor() { return this.A == 0 ? Color.red : Color.white; }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ public bool HideProperties;
+
+ [HideIf("HideProperties")]
+ public int MyInt;
+
+ [HideIf("HideProperties", false)]
+ public string MyString;
+
+ public SomeEnum SomeEnumField;
+
+ [HideIf("SomeEnumField", SomeEnum.SomeEnumMember)]
+ public string SomeString;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [HideIf("MyVisibleFunction")]
+ public int MyHideableField;
+
+ private bool MyVisibleFunction()
+ {
+ return !this.gameObject.activeInHierarchy;
+ }
+ }
+
+ HideIfGroup allows for showing or hiding a group of properties based on a condition.
@@ -911,6 +1812,31 @@
+ public class MyComponent : MonoBehaviour
+ {
+ [HideInEditorMode]
+ public int MyInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [HideInPlayMode]
+ public int MyInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [HideLabel]
+ public GameObject MyGameObjectWithoutLabel;
+ }
+
+
+ [HideMonoScript]
+ public class MyComponent : MonoBehaviour
+ {
+ // The Script property will not be shown for this component in the inspector
+ }
+
+
+ public class MyComponent : SerializedMonoBehaviour
+ {
+ [Header("Hidden Object Pickers")]
+ [Indent]
+ [HideReferenceObjectPicker]
+ public MyCustomReferenceType OdinSerializedProperty1;
+
+ [Indent]
+ [HideReferenceObjectPicker]
+ public MyCustomReferenceType OdinSerializedProperty2;
+
+ [Indent]
+ [Header("Shown Object Pickers")]
+ public MyCustomReferenceType OdinSerializedProperty3;
+
+ [Indent]
+ public MyCustomReferenceType OdinSerializedProperty4;
+
+ public class MyCustomReferenceType
+ {
+ public int A;
+ public int B;
+ public int C;
+ }
+ }
+
+
+ // The width can either be specified as percentage or pixels.
+ // All values between 0 and 1 will be treated as a percentage.
+ // If the width is 0 the column will be automatically sized.
+ // Margin-left and right can only be specified in pixels.
+
+ public class HorizontalGroupAttributeExamples : MonoBehaviour
+ {
+ [HorizontalGroup]
+ public int A;
+
+ [HideLabel, LabelWidth (150)]
+ [HorizontalGroup(150)]
+ public LayerMask B;
+
+ // LabelWidth can be helpfull when dealing with HorizontalGroups.
+ [HorizontalGroup("Group 1"), LabelWidth(15)]
+ public int C;
+
+ [HorizontalGroup("Group 1"), LabelWidth(15)]
+ public int D;
+
+ [HorizontalGroup("Group 1"), LabelWidth(15)]
+ public int E;
+
+ // Having multiple properties in a column can be achived using multiple groups. Checkout the "Combining Group Attributes" example.
+ [HorizontalGroup("Split", 0.5f, PaddingRight = 15)]
+ [BoxGroup("Split/Left"), LabelWidth(15)]
+ public int L;
+
+ [BoxGroup("Split/Right"), LabelWidth(15)]
+ public int M;
+
+ [BoxGroup("Split/Left"), LabelWidth(15)]
+ public int N;
+
+ [BoxGroup("Split/Right"), LabelWidth(15)]
+ public int O;
+
+ // Horizontal Group also has supprot for: Title, MarginLeft, MarginRight, PaddingLeft, PaddingRight, MinWidth and MaxWidth.
+ [HorizontalGroup("MyButton", MarginLeft = 0.25f, MarginRight = 0.25f)]
+ public void SomeButton()
+ {
+
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [Indent]
+ public int IndentedInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [InfoBox("This is an int property")]
+ public int MyInt;
+
+ [InfoBox("This info box is a warning", InfoMessageType.Warning)]
+ public float MyFloat;
+
+ [InfoBox("This info box is an error", InfoMessageType.Error)]
+ public object MyObject;
+
+ [InfoBox("This info box is just a box", InfoMessageType.None)]
+ public Vector3 MyVector;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [InfoBox("This info box is hidden by an instance field.", "InstanceShowInfoBoxField")]
+ public int MyInt;
+ public bool InstanceShowInfoBoxField;
+
+ [InfoBox("This info box is hideable by a static field.", "StaticShowInfoBoxField")]
+ public float MyFloat;
+ public static bool StaticShowInfoBoxField;
+
+ [InfoBox("This info box is hidden by an instance property.", "InstanceShowInfoBoxProperty")]
+ public int MyOtherInt;
+ public bool InstanceShowInfoBoxProperty { get; set; }
+
+ [InfoBox("This info box is hideable by a static property.", "StaticShowInfoBoxProperty")]
+ public float MyOtherFloat;
+ public static bool StaticShowInfoBoxProperty { get; set; }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [InfoBox("This info box is hidden by an instance function.", "InstanceShowFunction")]
+ public int MyInt;
+ public bool InstanceShowFunction()
+ {
+ return this.MyInt == 0;
+ }
+
+ [InfoBox("This info box is hidden by a static function.", "StaticShowFunction")]
+ public short MyShort;
+ public bool StaticShowFunction()
+ {
+ return true;
+ }
+
+ // You can also specify a function with the same type of parameter.
+ // Use this to specify the same function, for multiple different properties.
+ [InfoBox("This info box is hidden by an instance function with a parameter.", "InstanceShowParameterFunction")]
+ public GameObject MyGameObject;
+ public bool InstanceShowParameterFunction(GameObject property)
+ {
+ return property != null;
+ }
+
+ [InfoBox("This info box is hidden by a static function with a parameter.", "StaticShowParameterFunction")]
+ public Vector3 MyVector;
+ public bool StaticShowParameterFunction(Vector3 property)
+ {
+ return property.magnitude == 0f;
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ // Adds a button to the end of the A property.
+ [InlineButton("MyFunction")]
+ public int A;
+
+ // This is example demonstrates how you can change the label of the button.
+ // InlineButton also supports refering to string members with $.
+ [InlineButton("MyFunction", "Button")]
+ public int B;
+
+ private void MyFunction()
+ {
+ // ...
+ }
+ }
+
+
+ public class InlineEditorExamples : MonoBehaviour
+ {
+ [DisableInInlineEditors]
+ public Vector3 DisabledInInlineEditors;
+
+ [HideInInlineEditors]
+ public Vector3 HiddenInInlineEditors;
+
+ [InlineEditor]
+ public Transform InlineComponent;
+
+ [InlineEditor(InlineEditorModes.FullEditor)]
+ public Material FullInlineEditor;
+
+ [InlineEditor(InlineEditorModes.GUIAndHeader)]
+ public Material InlineMaterial;
+
+ [InlineEditor(InlineEditorModes.SmallPreview)]
+ public Material[] InlineMaterialList;
+
+ [InlineEditor(InlineEditorModes.LargePreview)]
+ public GameObject InlineObjectPreview;
+
+ [InlineEditor(InlineEditorModes.LargePreview)]
+ public Mesh InlineMeshPreview;
+ }
+
+
+ public MyComponent : MonoBehaviour
+ {
+ [LabelText("1")]
+ public int MyInt1;
+
+ [LabelText("2")]
+ public int MyInt2;
+
+ [LabelText("3")]
+ public int MyInt3;
+ }
+
+
+ [ListDrawerSettings(HideAddButton = true, OnTitleBarGUI = "DrawTitleBarGUI")]
+ public List<MyType> SomeList;
+
+ #if UNITY_EDITOR
+ private void DrawTitleBarGUI()
+ {
+ if (SirenixEditorGUI.ToolbarButton(EditorIcons.Plus))
+ {
+ this.SomeList.Add(new MyType());
+ }
+ }
+ #endif
+
+
+ public class Car : MonoBehaviour
+ {
+ // The speed of the car must be less than or equal to 200.
+ [MaxValue(200)]
+ public float Speed;
+ }
+
+
+ public class Health : MonoBehaviour
+ {
+ // The speed value must be between 0 and 200.
+ [MinValue(0), MaxValue(200)]
+ public float Speed;
+ }
+
+
+ public class Player : MonoBehaviour
+ {
+ [MinMaxSlider(4, 5)]
+ public Vector2 SpawnRadius;
+ }
+
+
+ public class Player : MonoBehaviour
+ {
+ // The life value must be set to at least 1.
+ [MinValue(1)]
+ public int Life;
+ }
+
+
+ public class Health : MonoBehaviour
+ {
+ // The health value must be between 0 and 100.
+ [MinValue(0), MaxValue(100)]
+ public float Health;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [MultiLineProperty]
+ public string MyString;
+
+ [ShowInInspector, MultiLineProperty(10)]
+ public string PropertyString;
+ }
+
+
+ public MyComponent : MonoBehaviour
+ {
+ [OnInspectorGUI]
+ private void MyInspectorGUI()
+ {
+ GUILayout.Label("Label drawn from callback");
+ }
+ }
+
+
+ public MyComponent : MonoBehaviour
+ {
+ [OnInspectorGUI("MyInspectorGUI", false)]
+ public int MyField;
+
+ private void MyInspectorGUI()
+ {
+ GUILayout.Label("Label before My Field property");
+ }
+ }
+
+
+ public MyComponent : MonoBehaviour
+ {
+ [OnInspectorGUI("GUIBefore", "GUIAfter")]
+ public int MyField;
+
+ private void GUIBefore()
+ {
+ GUILayout.Label("Label before My Field property");
+ }
+
+ private void GUIAfter()
+ {
+ GUILayout.Label("Label after My Field property");
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [OnValueChanged("MyCallback")]
+ public int MyInt;
+
+ private void MyCallback()
+ {
+ // ..
+ }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [OnValueChanged("OnPrefabChange")]
+ public GameObject MyPrefab;
+
+ // RigidBody component of MyPrefab.
+ [SerializeField, HideInInspector]
+ private RigidBody myPrefabRigidbody;
+
+ private void OnPrefabChange()
+ {
+ if(MyPrefab != null)
+ {
+ myPrefabRigidbody = MyPrefab.GetComponent<Rigidbody>();
+ }
+ else
+ {
+ myPrefabRigidbody = null;
+ }
+ }
+ }
+
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
+ public class BoxGroupAttribute : PropertyGroupAttribute
+ {
+ public string Label { get; private set; }
+ public bool ShowLabel { get; private set; }
+ public bool CenterLabel { get; private set; }
+
+ public BoxGroupAttribute(string group, bool showLabel = true, bool centerLabel = false, float order = 0)
+ : base(group, order)
+ {
+ this.Label = group;
+ this.ShowLabel = showLabel;
+ this.CenterLabel = centerLabel;
+ }
+
+ protected override void CombineValuesWith(PropertyGroupAttribute other)
+ {
+ // The given attribute parameter is *guaranteed* to be of type BoxGroupAttribute.
+ var attr = other as BoxGroupAttribute;
+
+ // If this attribute has no label, we the other group's label, thus preserving the label across combines.
+ if (this.Label == null)
+ {
+ this.Label = attr.Label;
+ }
+
+ // Combine ShowLabel and CenterLabel parameters.
+ this.ShowLabel |= attr.ShowLabel;
+ this.CenterLabel |= attr.CenterLabel;
+ }
+ }
+
+ protected override void CombineValuesWith(PropertyGroupAttribute other) { this.Title = this.Title ?? (other as MyGroupAttribute).Title; }
+ protected override void CombineValuesWith(PropertyGroupAttribute other)
+ {
+ // The given attribute parameter is *guaranteed* to be of type BoxGroupAttribute.
+ var attr = other as BoxGroupAttribute;
+
+ // If this attribute has no label, we the other group's label, thus preserving the label across combines.
+ if (this.Label == null)
+ {
+ this.Label = attr.Label;
+ }
+
+ // Combine ShowLabel and CenterLabel parameters.
+ this.ShowLabel |= attr.ShowLabel;
+ this.CenterLabel |= attr.CenterLabel;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [PropertyOrder(1)]
+ public int MySecondProperty;
+
+ [PropertyOrder(-1)]
+ public int MyFirstProperty;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [PropertyTooltip("This is an int property.")]
+ public int MyField;
+
+ [ShowInInspector, PropertyTooltip("This is another int property.")]
+ public int MyProperty { get; set; }
+ }
+
+
+ public class Health : MonoBehaviour
+ {
+ public int MaxHealth;
+
+ [ReadOnly]
+ public int CurrentHealth;
+ }
+
+
+ public class Health : MonoBehaviour
+ {
+ public int MaxHealth;
+
+ [ShowInInspector, ReadOnly]
+ private int currentHealth;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [Required]
+ public GameObject MyPrefab;
+
+ [Required(InfoMessageType.Warning)]
+ public Texture2D MyTexture;
+
+ [Required("MyMesh is nessessary for this component.")]
+ public Mesh MyMesh;
+
+ [Required("MyTransform might be important.", InfoMessageType.Info)]
+ public Transform MyTransform;
+ }
+
+
+ public MyComponent : MonoBehaviour
+ {
+ [SceneObjectsOnly]
+ public GameObject MyPrefab;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [ShowDrawerChain]
+ public int IndentedInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ public bool ShowProperties;
+
+ [ShowIf("showProperties")]
+ public int MyInt;
+
+ [ShowIf("showProperties", false)]
+ public string MyString;
+
+ public SomeEnum SomeEnumField;
+
+ [ShowIf("SomeEnumField", SomeEnum.SomeEnumMember)]
+ public string SomeString;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [ShowIf("MyVisibleFunction")]
+ public int MyHideableField;
+
+ private bool MyVisibleFunction()
+ {
+ return this.gameObject.activeInHierarchy;
+ }
+ }
+
+ ShowIfGroup allows for showing or hiding a group of properties based on a condition.
@@ -1757,11 +4620,43 @@
+ public class MyComponent : MonoBehaviour
+ {
+ [ShowInInspector]
+ private int myField;
+
+ [ShowInInspector]
+ public int MyProperty { get; set; }
+ }
+
+
+ public class NamedValue<T>
+ {
+ public string Name;
+
+ // The Range attribute will be applied if T is compatible with it, but if T is not compatible, an error will not be shown.
+ [SuppressInvalidAttributeError, Range(0, 10)]
+ public T Value;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [TabGroup("First")]
+ public int MyFirstInt;
+
+ [TabGroup("First")]
+ public int AnotherInt;
+
+ [TabGroup("Second")]
+ public int MySecondInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [TabGroup("A", "FirstGroup")]
+ public int FirstGroupA;
+
+ [TabGroup("B", "FirstGroup")]
+ public int FirstGroupB;
+
+ // The second tab group has been configured to have constant height across all tabs.
+ [TabGroup("A", "SecondGroup", true)]
+ public int SecondgroupA;
+
+ [TabGroup("B", "SecondGroup")]
+ public int SecondGroupB;
+
+ [TabGroup("B", "SecondGroup")]
+ public int AnotherInt;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [TabGroup("ParentGroup", "First Tab")]
+ public int A;
+
+ [TabGroup("ParentGroup", "Second Tab")]
+ public int B;
+
+ // Specify 'First Tab' as a group, and another child group to the 'First Tab' group.
+ [TabGroup("ParentGroup/First Tab/InnerGroup", "Inside First Tab A")]
+ public int C;
+
+ [TabGroup("ParentGroup/First Tab/InnerGroup", "Inside First Tab B")]
+ public int D;
+
+ [TabGroup("ParentGroup/Second Tab/InnerGroup", "Inside Second Tab")]
+ public int E;
+ }
+
+
+ [TableMatrix(SquareCells = true, Labels = "GetLabel")]
+ public int[,] ChessBoard = new int[8, 8];
+
+ private (string, LabelDirection) GetLabel(int[,] array, TableAxis axis, int index)
+ {
+ var chessFileLetters = "ABCDEFGH";
+
+ switch (axis)
+ {
+ case TableAxis.Y:
+ return ((array.GetLength(1) - index).ToString(), LabelDirection.LeftToRight);
+ case TableAxis.X:
+ return (chessFileLetters[index].ToString(), LabelDirection.TopToBottom);
+ default:
+ return (index.ToString(), LabelDirection.LeftToRight);
+ }
+ }
+
+
+ public class TitleExamples : MonoBehaviour
+ {
+ [Title("Titles and Headers")]
+ [InfoBox(
+ "The Title attribute has the same purpose as Unity's Header attribute," +
+ "but it also supports properties, and methods." +
+ "\n\nTitle also offers more features such as subtitles, options for horizontal underline, bold text and text alignment." +
+ "\n\nBoth attributes, with Odin, supports either static strings, or refering to members strings by adding a $ in front.")]
+ public string MyTitle = "My Dynamic Title";
+ public string MySubtitle = "My Dynamic Subtitle";
+
+ [Title("Static title")]
+ public int C;
+ public int D;
+
+ [Title("Static title", "Static subtitle")]
+ public int E;
+ public int F;
+
+ [Title("$MyTitle", "$MySubtitle")]
+ public int G;
+ public int H;
+
+ [Title("Non bold title", "$MySubtitle", bold: false)]
+ public int I;
+ public int J;
+
+ [Title("Non bold title", "With no line seperator", horizontalLine: false, bold: false)]
+ public int K;
+ public int L;
+
+ [Title("$MyTitle", "$MySubtitle", TitleAlignments.Right)]
+ public int M;
+ public int N;
+
+ [Title("$MyTitle", "$MySubtitle", TitleAlignments.Centered)]
+ public int O;
+ public int P;
+
+ [Title("$Combined", titleAlignment: TitleAlignments.Centered)]
+ public int Q;
+ public int R;
+
+ [ShowInInspector]
+ [Title("Title on a Property")]
+ public int S { get; set; }
+
+ [Title("Title on a Method")]
+ [Button]
+ public void DoNothing()
+ { }
+
+ public string Combined { get { return this.MyTitle + " - " + this.MySubtitle; } }
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [Toggle("Enabled")]
+ public MyToggleable MyToggler = new MyToggleable();
+ }
+
+ public class MyToggleable
+ {
+ public bool Enabled;
+
+ public int MyValue;
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ // This attribute has a title specified for the group. The title only needs to be applied to a single attribute for a group.
+ [ToggleGroup("FirstToggle", order: -1, groupTitle: "First")]
+ public bool FirstToggle;
+
+ [ToggleGroup("FirstToggle")]
+ public int MyInt;
+
+ // This group specifies a member string as the title of the group. A property or a function can also be used.
+ [ToggleGroup("SecondToggle", titleStringMemberName: "SecondGroupTitle")]
+ public bool SecondToggle { get; set; }
+
+ [ToggleGroup("SecondToggle")]
+ public float MyFloat;
+
+ [HideInInspector]
+ public string SecondGroupTitle = "Second";
+ }
+
+
+ public class MyComponent : MonoBehaviour
+ {
+ [ToggleLeft]
+ public bool MyBoolean;
+ }
+
+
- public class MyComponent : MonoBehaviour
- {
- [HorizontalGroup("Split")]
- [VerticalGroup("Split/Left")]
- public Vector3 Vector;
-
- [VerticalGroup("Split/Left")]
- public GameObject First;
-
- [VerticalGroup("Split/Left")]
- public GameObject Second;
-
- [VerticalGroup("Split/Right", PaddingTop = 18f)]
- public int A;
-
- [VerticalGroup("Split/Right")]
- public int B;
- }
-
-
- public class MyCustomClass : ISearchFilterable
- {
- public bool SearchEnabled;
- public string MyStr;
-
- public bool IsMatch(string searchString)
- {
- if (SearchEnabled)
- {
- return MyStr.Contains(searchString);
- }
-
- return false;
- }
- }
-
-
- public class ColorPaletteExamples : MonoBehaviour
- {
- [ColorPalette]
- public Color ColorOptions;
-
- [ColorPalette("Underwater")]
- public Color UnderwaterColor;
-
- [ColorPalette("Fall"), HideLabel]
- public Color WideColorPalette;
-
- [ColorPalette("My Palette")]
- public Color MyColor;
-
- [ColorPalette("Clovers")]
- public Color[] ColorArray;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [CustomContextMenu("My custom option", "MyAction")]
- public Vector3 MyVector;
-
- private void MyAction()
- {
- MyVector = Random.onUnitSphere;
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [DetailedInfoBox("This is a message", "Here is some more details about that message")]
- public int MyInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [DisableContextMenu]
- public Vector3 MyVector;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- public bool DisableProperty;
-
- [DisableIf("DisableProperty")]
- public int MyInt;
-
- public SomeEnum SomeEnumField;
-
- [DisableIf("SomeEnumField", SomeEnum.SomeEnumMember)]
- public string SomeString;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [EnableIf("MyDisableFunction")]
- public int MyInt;
-
- private bool MyDisableFunction()
- {
- // ...
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [DisableInEditorMode]
- public int MyInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [DisableInPlayMode]
- public int MyInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [DisplayAsString]
- public string MyInt = 5;
-
- // You can combine with to display a message in the inspector.
- [DisplayAsString, HideLabel]
- public string MyMessage = "This string will be displayed as text in the inspector";
-
- [DisplayAsString(false)]
- public string InlineMessage = "This string is very long, but has been configured to not overflow.";
- }
-
-
- [DontApplyToListElements]
- [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
- public sealed class VisibleIfAttribute : Attribute
- {
- public string MemberName { get; private set; }
-
- public VisibleIfAttribute(string memberName)
- {
- this.MemberName = memberName;
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- // Adds a button to the end of the A property.
- [InlineButton("MyFunction")]
- public int A;
-
- // This is example demonstrates how you can change the label of the button.
- // InlineButton also supports refering to string members with $.
- [InlineButton("MyFunction", "Button")]
- public int B;
-
- private void MyFunction()
- {
- // ...
- }
- }
-
-
- public class MyComponent
- {
- [ShowForPrefabOnlyAttribute]
- public int MyInt;
- }
-
-
- public class MyComponent
- {
- [EnableForPrefabOnly]
- public int MyInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- public bool EnableProperty;
-
- [EnableIf("EnableProperty")]
- public int MyInt;
-
- public SomeEnum SomeEnumField;
-
- [EnableIf("SomeEnumField", SomeEnum.SomeEnumMember)]
- public string SomeString;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [EnableIf("MyEnableFunction")]
- public int MyInt;
-
- private bool MyEnableFunction()
- {
- // ...
- }
- }
-
-
- public class MyComponent : MonoBehvaiour
- {
- [EnumToggleButtons]
- public MyBitmaskEnum MyBitmaskEnum;
-
- [EnumToggleButtons]
- public MyEnum MyEnum;
- }
-
- [Flags]
- public enum MyBitmaskEnum
- {
- A = 1 << 1, // 1
- B = 1 << 2, // 2
- C = 1 << 3, // 4
- ALL = A | B | C
- }
-
- public enum MyEnum
- {
- A,
- B,
- C
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- public bool HideProperties;
-
- [HideIf("HideProperties")]
- public int MyInt;
-
- [HideIf("HideProperties", false)]
- public string MyString;
-
- public SomeEnum SomeEnumField;
-
- [HideIf("SomeEnumField", SomeEnum.SomeEnumMember)]
- public string SomeString;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [HideIf("MyVisibleFunction")]
- public int MyHideableField;
-
- private bool MyVisibleFunction()
- {
- return !this.gameObject.activeInHierarchy;
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [HideInPlayMode]
- public int MyInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [HideInEditorMode]
- public int MyInt;
- }
-
-
- [HideMonoScript]
- public class MyComponent : MonoBehaviour
- {
- // The Script property will not be shown for this component in the inspector
- }
-
-
- public class MyComponent : SerializedMonoBehaviour
- {
- [Header("Hidden Object Pickers")]
- [Indent]
- [HideReferenceObjectPicker]
- public MyCustomReferenceType OdinSerializedProperty1;
-
- [Indent]
- [HideReferenceObjectPicker]
- public MyCustomReferenceType OdinSerializedProperty2;
-
- [Indent]
- [Header("Shown Object Pickers")]
- public MyCustomReferenceType OdinSerializedProperty3;
-
- [Indent]
- public MyCustomReferenceType OdinSerializedProperty4;
-
- public class MyCustomReferenceType
- {
- public int A;
- public int B;
- public int C;
- }
- }
-
-
- // The width can either be specified as percentage or pixels.
- // All values between 0 and 1 will be treated as a percentage.
- // If the width is 0 the column will be automatically sized.
- // Margin-left and right can only be specified in pixels.
-
- public class HorizontalGroupAttributeExamples : MonoBehaviour
- {
- [HorizontalGroup]
- public int A;
-
- [HideLabel, LabelWidth (150)]
- [HorizontalGroup(150)]
- public LayerMask B;
-
- // LabelWidth can be helpfull when dealing with HorizontalGroups.
- [HorizontalGroup("Group 1"), LabelWidth(15)]
- public int C;
-
- [HorizontalGroup("Group 1"), LabelWidth(15)]
- public int D;
-
- [HorizontalGroup("Group 1"), LabelWidth(15)]
- public int E;
-
- // Having multiple properties in a column can be achived using multiple groups. Checkout the "Combining Group Attributes" example.
- [HorizontalGroup("Split", 0.5f, PaddingRight = 15)]
- [BoxGroup("Split/Left"), LabelWidth(15)]
- public int L;
-
- [BoxGroup("Split/Right"), LabelWidth(15)]
- public int M;
-
- [BoxGroup("Split/Left"), LabelWidth(15)]
- public int N;
-
- [BoxGroup("Split/Right"), LabelWidth(15)]
- public int O;
-
- // Horizontal Group also has supprot for: Title, MarginLeft, MarginRight, PaddingLeft, PaddingRight, MinWidth and MaxWidth.
- [HorizontalGroup("MyButton", MarginLeft = 0.25f, MarginRight = 0.25f)]
- public void SomeButton()
- {
-
- }
- }
-
-
- public class InlineEditorExamples : MonoBehaviour
- {
- [DisableInInlineEditors]
- public Vector3 DisabledInInlineEditors;
-
- [HideInInlineEditors]
- public Vector3 HiddenInInlineEditors;
-
- [InlineEditor]
- public Transform InlineComponent;
-
- [InlineEditor(InlineEditorModes.FullEditor)]
- public Material FullInlineEditor;
-
- [InlineEditor(InlineEditorModes.GUIAndHeader)]
- public Material InlineMaterial;
-
- [InlineEditor(InlineEditorModes.SmallPreview)]
- public Material[] InlineMaterialList;
-
- [InlineEditor(InlineEditorModes.LargePreview)]
- public GameObject InlineObjectPreview;
-
- [InlineEditor(InlineEditorModes.LargePreview)]
- public Mesh InlineMeshPreview;
- }
-
-
- public class NamedValue<T>
- {
- public string Name;
-
- // The Range attribute will be applied if T is compatible with it, but if T is not compatible, an error will not be shown.
- [SuppressInvalidAttributeError, Range(0, 10)]
- public T Value;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ShowDrawerChain]
- public int IndentedInt;
- }
-
+
+
public class MyComponent : MonoBehaviour
- {
- [FoldoutGroup("MyGroup")]
- public int A;
+ {
+ [ValidateInput("ValidateInput")]
+ public float Speed;
- [FoldoutGroup("MyGroup")]
- public int B;
+ // Specify custom output message and message type.
+ [ValidateInput("ValidateInput", "Health must be more than 0!", InfoMessageType.Warning)]
+ public float Health;
- [FoldoutGroup("MyGroup")]
- public int C;
- }
+ private bool ValidateInput(float property)
+ {
+ return property > 0f;
+ }
+ }
public class MyComponent : MonoBehaviour
- {
- [FoldoutGroup("First")]
- public int A;
-
- [FoldoutGroup("First")]
- public int B;
-
- [FoldoutGroup("Second")]
- public int C;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [GUIColor(1f, 0f, 0f)]
- public int A;
-
- [GUIColor(1f, 0.5f, 0f, 0.2f)]
- public int B;
-
- [GUIColor("GetColor")]
- public int C;
-
- private Color GetColor() { return this.A == 0 ? Color.red : Color.white; }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [HideLabel]
- public GameObject MyGameObjectWithoutLabel;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [Indent]
- public int IndentedInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [InfoBox("This is an int property")]
+ {
+ [ValidateInput("StaticValidateFunction")]
public int MyInt;
- [InfoBox("This info box is a warning", InfoMessageType.Warning)]
- public float MyFloat;
-
- [InfoBox("This info box is an error", InfoMessageType.Error)]
- public object MyObject;
-
- [InfoBox("This info box is just a box", InfoMessageType.None)]
- public Vector3 MyVector;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [InfoBox("This info box is hidden by an instance field.", "InstanceShowInfoBoxField")]
- public int MyInt;
- public bool InstanceShowInfoBoxField;
-
- [InfoBox("This info box is hideable by a static field.", "StaticShowInfoBoxField")]
- public float MyFloat;
- public static bool StaticShowInfoBoxField;
-
- [InfoBox("This info box is hidden by an instance property.", "InstanceShowInfoBoxProperty")]
- public int MyOtherInt;
- public bool InstanceShowInfoBoxProperty { get; set; }
-
- [InfoBox("This info box is hideable by a static property.", "StaticShowInfoBoxProperty")]
- public float MyOtherFloat;
- public static bool StaticShowInfoBoxProperty { get; set; }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [InfoBox("This info box is hidden by an instance function.", "InstanceShowFunction")]
- public int MyInt;
- public bool InstanceShowFunction()
+ private static bool StaticValidateFunction(int property)
{
- return this.MyInt == 0;
+ return property != 0;
}
-
- [InfoBox("This info box is hidden by a static function.", "StaticShowFunction")]
- public short MyShort;
- public bool StaticShowFunction()
- {
- return true;
- }
-
- // You can also specify a function with the same type of parameter.
- // Use this to specify the same function, for multiple different properties.
- [InfoBox("This info box is hidden by an instance function with a parameter.", "InstanceShowParameterFunction")]
- public GameObject MyGameObject;
- public bool InstanceShowParameterFunction(GameObject property)
- {
- return property != null;
- }
-
- [InfoBox("This info box is hidden by a static function with a parameter.", "StaticShowParameterFunction")]
- public Vector3 MyVector;
- public bool StaticShowParameterFunction(Vector3 property)
- {
- return property.magnitude == 0f;
- }
- }
-
-
- public class Player : MonoBehaviour
- {
- [MinMaxSlider(4, 5)]
- public Vector2 SpawnRadius;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ToggleLeft]
- public bool MyBoolean;
- }
-
-
- public MyComponent : MonoBehaviour
- {
- [LabelText("1")]
- public int MyInt1;
-
- [LabelText("2")]
- public int MyInt2;
-
- [LabelText("3")]
- public int MyInt3;
- }
-
-
- [ListDrawerSettings(HideAddButton = true, OnTitleBarGUI = "DrawTitleBarGUI")]
- public List<MyType> SomeList;
-
- #if UNITY_EDITOR
- private void DrawTitleBarGUI()
- {
- if (SirenixEditorGUI.ToolbarButton(EditorIcons.Plus))
- {
- this.SomeList.Add(new MyType());
- }
- }
- #endif
-
-
- public class Car : MonoBehaviour
- {
- // The speed of the car must be less than or equal to 200.
- [MaxValue(200)]
- public float Speed;
- }
-
-
- public class Health : MonoBehaviour
- {
- // The speed value must be between 0 and 200.
- [MinValue(0), MaxValue(200)]
- public float Speed;
- }
-
-
- public class Player : MonoBehaviour
- {
- // The life value must be set to at least 1.
- [MinValue(1)]
- public int Life;
- }
-
-
- public class Health : MonoBehaviour
- {
- // The health value must be between 0 and 100.
- [MinValue(0), MaxValue(100)]
- public float Health;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [MultiLineProperty]
- public string MyString;
-
- [ShowInInspector, MultiLineProperty(10)]
- public string PropertyString;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [PropertyTooltip("This is an int property.")]
- public int MyField;
-
- [ShowInInspector, PropertyTooltip("This is another int property.")]
- public int MyProperty { get; set; }
- }
-
-
- public class Health : MonoBehaviour
- {
- public int MaxHealth;
-
- [ReadOnly]
- public int CurrentHealth;
- }
-
-
- public class Health : MonoBehaviour
- {
- public int MaxHealth;
-
- [ShowInInspector, ReadOnly]
- private int currentHealth;
- }
-
-
- public MyComponent : MonoBehaviour
- {
- [OnInspectorGUI]
- private void MyInspectorGUI()
- {
- GUILayout.Label("Label drawn from callback");
- }
- }
-
-
- public MyComponent : MonoBehaviour
- {
- [OnInspectorGUI("MyInspectorGUI", false)]
- public int MyField;
-
- private void MyInspectorGUI()
- {
- GUILayout.Label("Label before My Field property");
- }
- }
-
-
- public MyComponent : MonoBehaviour
- {
- [OnInspectorGUI("GUIBefore", "GUIAfter")]
- public int MyField;
-
- private void GUIBefore()
- {
- GUILayout.Label("Label before My Field property");
- }
-
- private void GUIAfter()
- {
- GUILayout.Label("Label after My Field property");
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [OnValueChanged("MyCallback")]
- public int MyInt;
-
- private void MyCallback()
- {
- // ..
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [OnValueChanged("OnPrefabChange")]
- public GameObject MyPrefab;
-
- // RigidBody component of MyPrefab.
- [SerializeField, HideInInspector]
- private RigidBody myPrefabRigidbody;
-
- private void OnPrefabChange()
- {
- if(MyPrefab != null)
- {
- myPrefabRigidbody = MyPrefab.GetComponent<Rigidbody>();
- }
- else
- {
- myPrefabRigidbody = null;
- }
- }
- }
-
-
- [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
- public class BoxGroupAttribute : PropertyGroupAttribute
- {
- public string Label { get; private set; }
- public bool ShowLabel { get; private set; }
- public bool CenterLabel { get; private set; }
-
- public BoxGroupAttribute(string group, bool showLabel = true, bool centerLabel = false, float order = 0)
- : base(group, order)
- {
- this.Label = group;
- this.ShowLabel = showLabel;
- this.CenterLabel = centerLabel;
- }
-
- protected override void CombineValuesWith(PropertyGroupAttribute other)
- {
- // The given attribute parameter is *guaranteed* to be of type BoxGroupAttribute.
- var attr = other as BoxGroupAttribute;
-
- // If this attribute has no label, we the other group's label, thus preserving the label across combines.
- if (this.Label == null)
- {
- this.Label = attr.Label;
- }
-
- // Combine ShowLabel and CenterLabel parameters.
- this.ShowLabel |= attr.ShowLabel;
- this.CenterLabel |= attr.CenterLabel;
- }
- }
-
- protected override void CombineValuesWith(PropertyGroupAttribute other) { this.Title = this.Title ?? (other as MyGroupAttribute).Title; }
- protected override void CombineValuesWith(PropertyGroupAttribute other)
- {
- // The given attribute parameter is *guaranteed* to be of type BoxGroupAttribute.
- var attr = other as BoxGroupAttribute;
-
- // If this attribute has no label, we the other group's label, thus preserving the label across combines.
- if (this.Label == null)
- {
- this.Label = attr.Label;
- }
-
- // Combine ShowLabel and CenterLabel parameters.
- this.ShowLabel |= attr.ShowLabel;
- this.CenterLabel |= attr.CenterLabel;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [PropertyOrder(1)]
- public int MySecondProperty;
-
- [PropertyOrder(-1)]
- public int MyFirstProperty;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [Required]
- public GameObject MyPrefab;
-
- [Required(InfoMessageType.Warning)]
- public Texture2D MyTexture;
-
- [Required("MyMesh is nessessary for this component.")]
- public Mesh MyMesh;
-
- [Required("MyTransform might be important.", InfoMessageType.Info)]
- public Transform MyTransform;
}
- public MyComponent : MonoBehaviour
- {
- [SceneObjectsOnly]
- public GameObject MyPrefab;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ShowInInspector]
- private int myField;
-
- [ShowInInspector]
- public int MyProperty { get; set; }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [TabGroup("First")]
- public int MyFirstInt;
-
- [TabGroup("First")]
- public int AnotherInt;
-
- [TabGroup("Second")]
- public int MySecondInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [TabGroup("A", "FirstGroup")]
- public int FirstGroupA;
-
- [TabGroup("B", "FirstGroup")]
- public int FirstGroupB;
-
- // The second tab group has been configured to have constant height across all tabs.
- [TabGroup("A", "SecondGroup", true)]
- public int SecondgroupA;
-
- [TabGroup("B", "SecondGroup")]
- public int SecondGroupB;
-
- [TabGroup("B", "SecondGroup")]
- public int AnotherInt;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [TabGroup("ParentGroup", "First Tab")]
- public int A;
-
- [TabGroup("ParentGroup", "Second Tab")]
- public int B;
-
- // Specify 'First Tab' as a group, and another child group to the 'First Tab' group.
- [TabGroup("ParentGroup/First Tab/InnerGroup", "Inside First Tab A")]
- public int C;
-
- [TabGroup("ParentGroup/First Tab/InnerGroup", "Inside First Tab B")]
- public int D;
-
- [TabGroup("ParentGroup/Second Tab/InnerGroup", "Inside Second Tab")]
- public int E;
- }
-
-
- public class TitleExamples : MonoBehaviour
+ public class MyComponent : MonoBehaviour
{
- [Title("Titles and Headers")]
- [InfoBox(
- "The Title attribute has the same purpose as Unity's Header attribute," +
- "but it also supports properties, and methods." +
- "\n\nTitle also offers more features such as subtitles, options for horizontal underline, bold text and text alignment." +
- "\n\nBoth attributes, with Odin, supports either static strings, or refering to members strings by adding a $ in front.")]
- public string MyTitle = "My Dynamic Title";
- public string MySubtitle = "My Dynamic Subtitle";
-
- [Title("Static title")]
- public int C;
- public int D;
-
- [Title("Static title", "Static subtitle")]
- public int E;
- public int F;
-
- [Title("$MyTitle", "$MySubtitle")]
- public int G;
- public int H;
-
- [Title("Non bold title", "$MySubtitle", bold: false)]
- public int I;
- public int J;
-
- [Title("Non bold title", "With no line seperator", horizontalLine: false, bold: false)]
- public int K;
- public int L;
-
- [Title("$MyTitle", "$MySubtitle", TitleAlignments.Right)]
- public int M;
- public int N;
-
- [Title("$MyTitle", "$MySubtitle", TitleAlignments.Centered)]
- public int O;
- public int P;
-
- [Title("$Combined", titleAlignment: TitleAlignments.Centered)]
- public int Q;
- public int R;
-
- [ShowInInspector]
- [Title("Title on a Property")]
- public int S { get; set; }
-
- [Title("Title on a Method")]
- [Button]
- public void DoNothing()
- { }
-
- public string Combined { get { return this.MyTitle + " - " + this.MySubtitle; } }
+ [HorizontalGroup("Split")]
+ [VerticalGroup("Split/Left")]
+ public Vector3 Vector;
+
+ [VerticalGroup("Split/Left")]
+ public GameObject First;
+
+ [VerticalGroup("Split/Left")]
+ public GameObject Second;
+
+ [VerticalGroup("Split/Right", PaddingTop = 18f)]
+ public int A;
+
+ [VerticalGroup("Split/Right")]
+ public int B;
}
- public class MyComponent : MonoBehaviour
- {
- [Toggle("Enabled")]
- public MyToggleable MyToggler = new MyToggleable();
- }
-
- public class MyToggleable
- {
- public bool Enabled;
-
- public int MyValue;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- // This attribute has a title specified for the group. The title only needs to be applied to a single attribute for a group.
- [ToggleGroup("FirstToggle", order: -1, groupTitle: "First")]
- public bool FirstToggle;
-
- [ToggleGroup("FirstToggle")]
- public int MyInt;
-
- // This group specifies a member string as the title of the group. A property or a function can also be used.
- [ToggleGroup("SecondToggle", titleStringMemberName: "SecondGroupTitle")]
- public bool SecondToggle { get; set; }
-
- [ToggleGroup("SecondToggle")]
- public float MyFloat;
-
- [HideInInspector]
- public string SecondGroupTitle = "Second";
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ValidateInput("ValidateInput")]
- public float Speed;
-
- // Specify custom output message and message type.
- [ValidateInput("ValidateInput", "Health must be more than 0!", InfoMessageType.Warning)]
- public float Health;
-
- private bool ValidateInput(float property)
- {
- return property > 0f;
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ValidateInput("StaticValidateFunction")]
- public int MyInt;
-
- private static bool StaticValidateFunction(int property)
- {
- return property != 0;
- }
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- public bool ShowProperties;
-
- [ShowIf("showProperties")]
- public int MyInt;
-
- [ShowIf("showProperties", false)]
- public string MyString;
-
- public SomeEnum SomeEnumField;
-
- [ShowIf("SomeEnumField", SomeEnum.SomeEnumMember)]
- public string SomeString;
- }
-
-
- public class MyComponent : MonoBehaviour
- {
- [ShowIf("MyVisibleFunction")]
- public int MyHideableField;
-
- private bool MyVisibleFunction()
- {
- return this.gameObject.activeInHierarchy;
- }
- }
-
-
+ public class MyCustomClass : ISearchFilterable
+ {
+ public bool SearchEnabled;
+ public string MyStr;
+
+ public bool IsMatch(string searchString)
+ {
+ if (SearchEnabled)
+ {
+ return MyStr.Contains(searchString);
+ }
+
+ return false;
+ }
+ }
+
+
- public class OdinMenuEditorWindowExample : OdinMenuEditorWindow
- {
- [SerializeField, HideLabel]
- private SomeData someData = new SomeData();
-
- protected override OdinMenuTree BuildMenuTree()
- {
- OdinMenuTree tree = new OdinMenuTree(supportsMultiSelect: true)
- {
- { "Home", this, EditorIcons.House }, // draws the someDataField in this case.
- { "Odin Settings", null, EditorIcons.SettingsCog },
- { "Odin Settings/Color Palettes", ColorPaletteManager.Instance, EditorIcons.EyeDropper },
- { "Odin Settings/AOT Generation", AOTGenerationConfig.Instance, EditorIcons.SmartPhone },
- { "Camera current", Camera.current },
- { "Some Class", this.someData }
- };
-
- tree.AddAllAssetsAtPath("More Odin Settings", SirenixAssetPaths.OdinEditorConfigsPath, typeof(ScriptableObject), true)
- .AddThumbnailIcons();
-
- tree.AddAssetAtPath("Odin Getting Started", SirenixAssetPaths.SirenixPluginPath + "Getting Started With Odin.asset");
-
- var customMenuItem = new OdinMenuItem(tree, "Menu Style", tree.DefaultMenuStyle);
- tree.MenuItems.Insert(2, customMenuItem);
-
- tree.Add("Menu/Items/Are/Created/As/Needed", new GUIContent());
- tree.Add("Menu/Items/Are/Created", new GUIContent("And can be overridden"));
-
- // As you can see, Odin provides a few ways to quickly add editors / objects to your menu tree.
- // The API also gives you full control over the selection, etc..
- // Make sure to check out the API Documentation for OdinMenuEditorWindow, OdinMenuTree and OdinMenuItem for more information on what you can do!
-
- return tree;
- }
- }
-
-
- OdinMenuTree tree = new OdinMenuTree(supportsMultiSelect: true)
- {
- { "Home", this, EditorIcons.House },
- { "Odin Settings", null, EditorIcons.SettingsCog },
- { "Odin Settings/Color Palettes", ColorPaletteManager.Instance, EditorIcons.EyeDropper },
- { "Odin Settings/AOT Generation", AOTGenerationConfig.Instance, EditorIcons.SmartPhone },
- { "Camera current", Camera.current },
- { "Some Class", this.someData }
- };
-
- tree.AddAllAssetsAtPath("Some Menu Item", "Some Asset Path", typeof(ScriptableObject), true)
- .AddThumbnailIcons();
-
- tree.AddAssetAtPath("Some Second Menu Item", "SomeAssetPath/SomeAssetFile.asset");
-
- var customMenuItem = new OdinMenuItem(tree, "Menu Style", tree.DefaultMenuStyle);
- tree.MenuItems.Insert(2, customMenuItem);
-
- tree.Add("Menu/Items/Are/Created/As/Needed", new GUIContent());
- tree.Add("Menu/Items/Are/Created", new GUIContent("And can be overridden"));
-
- OdinMenuTrees are typically used with
- // Draw stuff
- someTree.DrawMenuTree();
- // Draw stuff
- someTree.HandleKeybaordMenuNavigation();
-
-
- OdinMenuTree tree = new OdinMenuTree();
- tree.AddAllAssetsAtPath("Some Menu Item", "Some Asset Path", typeof(ScriptableObject), true)
- .AddThumbnailIcons();
- tree.AddAssetAtPath("Some Second Menu Item", "SomeAssetPath/SomeAssetFile.asset");
- // etc...
-
-
- KeyCode someEnumValue;
-
- [OnInspectorGUI]
- void OnInspectorGUI()
- {
- // Use the selector manually. See the documentation for OdinSelector for more information.
- if (GUILayout.Button("Open Enum Selector"))
- {
- EnumSelector<KeyCode> selector = new EnumSelector<KeyCode>();
- selector.SetSelection(this.someEnumValue);
- selector.SelectionConfirmed += selection => this.someEnumValue = selection.FirstOrDefault();
- selector.ShowInPopup(); // Returns the Odin Editor Window instance, in case you want to mess around with that as well.
- }
-
- // Draw an enum dropdown field which uses the EnumSelector popup:
- this.someEnumValue = EnumSelector<KeyCode>.DrawEnumField(new GUIContent("My Label"), this.someEnumValue);
- }
-
- // All Odin Selectors can be rendered anywhere with Odin. This includes the EnumSelector.
- EnumSelector<KeyCode> inlineSelector;
-
- [ShowInInspector]
- EnumSelector<KeyCode> InlineSelector
- {
- get { return this.inlineSelector ?? (this.inlineSelector = new EnumSelector<KeyCode>()); }
- set { }
- }
-
-
- SomeType someValue;
-
- [OnInspectorGUI]
- void OnInspectorGUI()
- {
- if (GUILayout.Button("Open Generic Selector Popup"))
- {
- List<SomeType> source = ...;
- GenericSelector<SomeType> selector = new GenericSelector<SomeType>("Title", false, x => x.Path, source);
- selector.SetSelection(this.someValue);
- selector.SelectionTree.Config.DrawSearchToolbar = false;
- selector.SelectionTree.DefaultMenuStyle.Height = 22;
- selector.SelectionConfirmed += selection => this.someValue = selection.FirstOrDefault()
- var window = selector.ShowInPopup();
- window.OnEndGUI += () => { EditorGUILayout.HelpBox("A quick way of injecting custom GUI to the editor window popup instance.", MessageType.Info); };
- window.OnClose += selector.SelectionTree.Selection.ConfirmSelection; // Confirm selection when window clses.
- }
- }
-
-
- public class MySelector : OdinSelector<SomeType>
- {
- private readonly List<SomeType> source;
- private readonly bool supportsMultiSelect;
-
- public MySelector(List<SomeType> source, bool supportsMultiSelect)
- {
- this.source = source;
- this.supportsMultiSelect = supportsMultiSelect;
- }
-
- protected override void BuildSelectionTree(OdinMenuTree tree)
- {
- tree.Config.DrawSearchToolbar = true;
- tree.Selection.SupportsMultiSelect = this.supportsMultiSelect;
-
- tree.Add("Defaults/None", null);
- tree.Add("Defaults/A", new SomeType());
- tree.Add("Defaults/B", new SomeType());
-
- tree.AddRange(this.source, x => x.Path, x => x.SomeTexture);
- }
-
- [OnInspectorGUI]
- private void DrawInfoAboutSelectedItem()
- {
- SomeType selected = this.GetCurrentSelection().FirstOrDefault();
-
- if (selected != null)
- {
- GUILayout.Label("Name: " + selected.Name);
- GUILayout.Label("Data: " + selected.Data);
- }
- }
- }
-
- Usage:
-
- void OnGUI()
- {
- if (GUILayout.Button("Open My Selector"))
- {
- List<SomeType> source = this.GetListOfThingsToSelectFrom();
- MySelector selector = new MySelector(source, false);
-
- selector.SetSelection(this.someValue);
-
- selector.SelectionCancelled += () => { }; // Occurs when the popup window is closed, and no slection was confirmed.
- selector.SelectionChanged += col => { };
- selector.SelectionConfirmed += col => this.someValue = col.FirstOrDefault();
-
- selector.ShowInPopup(); // Returns the Odin Editor Window instance, in case you want to mess around with that as well.
- }
- }
-
- // All Odin Selectors can be rendered anywhere with Odin.
- [ShowInInspector]
- MySelector inlineSelector;
-
-
- Type[] selectedTypes;
-
- void OnGUI()
- {
- // Use the selector manually. See the documentation for OdinSelector for more information.
- if (GUILayout.Button("Open My Selector"))
- {
- TypeSelector selector = new TypeSelector(customListOfTypes);
- TypeSelector selector = new TypeSelector(AssemblyTypeFlags.CustomTypes, supportsMultiSelect: true);
- selector.SetSelection(this.selectedTypes);
- selector.SelectionConfirmed += selection => this.selectedTypes = selection.ToArray();
- selector.ShowInPopup(); // Returns the Odin Editor Window instance, in case you want to mess around with that as well.
- }
- }
-
-
- private static Type currentSelectedType;
- private static IEnumerable<Type> currentSource;
- private static Func<Rect, OdinSelector<Type>> createTypeSelector = (rect) =>
- {
- TypeSelector selector = new TypeSelector(currentSource, false);
- selector.SetSelection(currentSelectedType);
- selector.ShowInPopup(rect);
- return selector;
- };
-
- public static Type DrawTypeSelectorDropdown(GUIContent label, Type selectedType, IEnumerable<Type> source)
- {
- currentSource = source;
- currentSelectedType = selectedType;
-
- var dropdownText = selectedType == null ? "None" : selectedType.GetNiceName();
- var selected = TypeSelector.DrawSelectorDropdown(label, dropdownText, createTypeSelector);
- if (selected != null && selected.Any())
- {
- selectedType = selected.FirstOrDefault();
- }
- return selectedType;
- }
-
-
- public class SomeWindow : OdinEditorWindow
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class CustomRangeAttribute : System.Attribute
{
- [MenuItem("My Game/Some Window")]
- private static void OpenWindow()
+ public float Min;
+ public float Max;
+
+ public CustomRangeAttribute(float min, float max)
{
- GetWindow<SomeWindow>().Show();
+ this.Min = min;
+ this.Max = max;
}
+ }
- [Button(ButtonSizes.Large)]
- public void SomeButton() { }
+ // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
- [TableList]
- public SomeType[] SomeTableData;
+ public sealed class CustomRangeAttributeDrawer : OdinAttributeDrawer<CustomRangeAttribute, float>
+ {
+ protected override void DrawPropertyLayout(GUIContent label)
+ {
+ this.ValueEntry.SmartValue = EditorGUILayout.Slider(label, this.ValueEntry.SmartValue, this.Attribute.Min, this.Attribute.Max);
+ }
+ }
+
+ // Usage:
+ public class MyComponent : MonoBehaviour
+ {
+ [CustomRangeAttribute(0, 1)]
+ public float MyFloat;
}
- public class DrawSomeSingletonInAnEditorWindow : OdinEditorWindow
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class GUITintColorAttribute : System.Attribute
{
- [MenuItem("My Game/Some Window")]
- private static void OpenWindow()
+ public Color Color;
+
+ public GUITintColorAttribute(float r, float g, float b, float a = 1)
{
- GetWindow<DrawSomeSingletonInAnEditorWindow>().Show();
+ this.Color = new Color(r, g, b, a);
+ }
+ }
+
+ // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
+
+ public sealed class GUITintColorAttributeDrawer : OdinAttributeDrawer<GUITintColorAttribute>
+ {
+ protected override void DrawPropertyLayout(GUIContent label)
+ {
+ Color prevColor = GUI.color;
+ GUI.color *= this.Attribute.Color;
+ this.CallNextDrawer(label);
+ GUI.color = prevColor;
+ }
+ }
+
+ // Usage:
+ public class MyComponent : MonoBehaviour
+ {
+ [GUITintColor(0, 1, 0)]
+ public float MyFloat;
+ }
+
+
+ [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
+ public sealed class MyCustomAttributeDrawer<T> : OdinAttributeDrawer<MyCustomAttribute, T> where T : class
+ {
+ public override bool CanDrawTypeFilter(Type type)
+ {
+ return type != typeof(string);
}
- protected override object GetTarget()
+ protected override void DrawPropertyLayout(GUIContent label)
{
- return MySingleton.Instance;
+ // Draw property here.
}
}
- private void InspectObjectInWindow()
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class CustomRangeAttribute : System.Attribute
{
- OdinEditorWindow.InspectObject(someObject);
+ public float Min;
+ public float Max;
+
+ public CustomRangeAttribute(float min, float max)
+ {
+ this.Min = min;
+ this.Max = max;
+ }
}
-
- private void InspectObjectInDropDownWithAutoHeight()
+
+ // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
+
+ public sealed class CustomRangeAttributeDrawer : OdinAttributeDrawer<CustomRangeAttribute, float>
{
- var btnRect = GUIHelper.GetCurrentLayoutRect();
- OdinEditorWindow.InspectObjectInDropDown(someObject, btnRect, btnRect.width);
+ protected override void DrawPropertyLayout(GUIContent label)
+ {
+ this.ValueEntry.SmartValue = EditorGUILayout.Slider(label, this.ValueEntry.SmartValue, this.Attribute.Min, this.Attribute.Max);
+ }
}
-
- private void InspectObjectInDropDown()
+
+ // Usage:
+ public class MyComponent : MonoBehaviour
{
- var btnRect = GUIHelper.GetCurrentLayoutRect();
- OdinEditorWindow.InspectObjectInDropDown(someObject, btnRect, new Vector2(btnRect.width, 100));
- }
-
- private void InspectObjectInACenteredWindow()
- {
- var window = OdinEditorWindow.InspectObject(someObject);
- window.position = GUIHelper.GetEditorWindowRect().AlignCenter(270, 200);
- }
-
- private void OtherStuffYouCanDo()
- {
- var window = OdinEditorWindow.InspectObject(this.someObject);
-
- window.position = GUIHelper.GetEditorWindowRect().AlignCenter(270, 200);
- window.titleContent = new GUIContent("Custom title", EditorIcons.RulerRect.Active);
- window.OnClose += () => Debug.Log("Window Closed");
- window.OnBeginGUI += () => GUILayout.Label("-----------");
- window.OnEndGUI += () => GUILayout.Label("-----------");
+ [CustomRangeAttribute(0, 1)]
+ public float MyFloat;
}
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class GUITintColorAttribute : System.Attribute
+ {
+ public Color Color;
+
+ public GUITintColorAttribute(float r, float g, float b, float a = 1)
+ {
+ this.Color = new Color(r, g, b, a);
+ }
+ }
+
+ // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
+
+ public sealed class GUITintColorAttributeDrawer : OdinAttributeDrawer<GUITintColorAttribute>
+ {
+ protected override void DrawPropertyLayout(GUIContent label)
+ {
+ Color prevColor = GUI.color;
+ GUI.color *= this.Attribute.Color;
+ this.CallNextDrawer(label);
+ GUI.color = prevColor;
+ }
+ }
+
+ // Usage:
+ public class MyComponent : MonoBehaviour
+ {
+ [GUITintColor(0, 1, 0)]
+ public float MyFloat;
+ }
+
+
+ [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
+ public class MyCustomAttributeDrawer<T> : OdinAttributeDrawer<MyCustomAttribute, T> where T : class
+ {
+ public override bool CanDrawTypeFilter(Type type)
+ {
+ return type != typeof(string);
+ }
+
+ protected override void DrawPropertyLayout(GUIContent label)
+ {
+ // Draw property here.
+ }
+ }
+
+
+ public class MyCustomBaseType
+ {
+
+ }
+
+ public class MyCustomType : MyCustomBaseType
+ {
+
+ }
+
+ // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
+
+ public sealed class MyCustomBaseTypeDrawer<T> : OdinValueDrawer<T> where T : MyCustomBaseType
+ {
+ protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
+ {
+ T value = entry.SmartValue;
+ // Draw your custom drawer here using GUILayout and EditorGUILAyout.
+ }
+ }
+
+ // Usage:
+ // Both values will be drawn using the MyCustomBaseTypeDrawer
+ public class MyComponent : SerializedMonoBehaviour
+ {
+ public MyCustomBaseType A;
+
+ public MyCustomType B;
+ }
+
+
+ // [OdinDrawer(OdinDrawerBehaviour.DrawProperty)] // default
+ // [OdinDrawer(OdinDrawerBehaviour.AppendDecorator)]
+ [OdinDrawer(OdinDrawerBehaviour.PrependDecorator)]
+ [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
+ public sealed class MyCustomTypeDrawer<T> : OdinValueDrawer<T> where T : MyCustomType
+ {
+ public override bool CanDrawTypeFilter(Type type)
+ {
+ return type != typeof(SomeType);
+ }
+
+ protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
+ {
+ T value = entry.SmartValue;
+ // Draw property here.
+ }
+ }
+
+
+
+ [AllowGUIEnabledForReadonly]
+ public sealed class SomeDrawerDrawer<T> : OdinValueDrawer<T> where T : class
+ {
+ }
+
+
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
- public class CustomRangeAttribute : System.Attribute
- {
- public float Min;
- public float Max;
-
- public CustomRangeAttribute(float min, float max)
- {
- this.Min = min;
- this.Max = max;
- }
- }
-
- // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
-
- public sealed class CustomRangeAttributeDrawer : OdinAttributeDrawer<CustomRangeAttribute, float>
- {
- protected override void DrawPropertyLayout(GUIContent label)
- {
- this.ValueEntry.SmartValue = EditorGUILayout.Slider(label, this.ValueEntry.SmartValue, this.Attribute.Min, this.Attribute.Max);
- }
- }
-
- // Usage:
- public class MyComponent : MonoBehaviour
- {
- [CustomRangeAttribute(0, 1)]
- public float MyFloat;
- }
-
-
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
- public class GUITintColorAttribute : System.Attribute
- {
- public Color Color;
-
- public GUITintColorAttribute(float r, float g, float b, float a = 1)
- {
- this.Color = new Color(r, g, b, a);
- }
- }
-
- // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
-
- public sealed class GUITintColorAttributeDrawer : OdinAttributeDrawer<GUITintColorAttribute>
- {
- protected override void DrawPropertyLayout(GUIContent label)
- {
- Color prevColor = GUI.color;
- GUI.color *= this.Attribute.Color;
- this.CallNextDrawer(label);
- GUI.color = prevColor;
- }
- }
-
- // Usage:
- public class MyComponent : MonoBehaviour
- {
- [GUITintColor(0, 1, 0)]
- public float MyFloat;
- }
-
-
- [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
- public sealed class MyCustomAttributeDrawer<T> : OdinAttributeDrawer<MyCustomAttribute, T> where T : class
- {
- public override bool CanDrawTypeFilter(Type type)
- {
- return type != typeof(string);
- }
-
- protected override void DrawPropertyLayout(GUIContent label)
- {
- // Draw property here.
- }
- }
-
-
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
- public class CustomRangeAttribute : System.Attribute
- {
- public float Min;
- public float Max;
-
- public CustomRangeAttribute(float min, float max)
- {
- this.Min = min;
- this.Max = max;
- }
- }
-
- // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
-
- public sealed class CustomRangeAttributeDrawer : OdinAttributeDrawer<CustomRangeAttribute, float>
- {
- protected override void DrawPropertyLayout(GUIContent label)
- {
- this.ValueEntry.SmartValue = EditorGUILayout.Slider(label, this.ValueEntry.SmartValue, this.Attribute.Min, this.Attribute.Max);
- }
- }
-
- // Usage:
- public class MyComponent : MonoBehaviour
- {
- [CustomRangeAttribute(0, 1)]
- public float MyFloat;
- }
-
-
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
- public class GUITintColorAttribute : System.Attribute
- {
- public Color Color;
-
- public GUITintColorAttribute(float r, float g, float b, float a = 1)
- {
- this.Color = new Color(r, g, b, a);
- }
- }
-
- // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
-
- public sealed class GUITintColorAttributeDrawer : OdinAttributeDrawer<GUITintColorAttribute>
- {
- protected override void DrawPropertyLayout(GUIContent label)
- {
- Color prevColor = GUI.color;
- GUI.color *= this.Attribute.Color;
- this.CallNextDrawer(label);
- GUI.color = prevColor;
- }
- }
-
- // Usage:
- public class MyComponent : MonoBehaviour
- {
- [GUITintColor(0, 1, 0)]
- public float MyFloat;
- }
-
-
- [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
- public class MyCustomAttributeDrawer<T> : OdinAttributeDrawer<MyCustomAttribute, T> where T : class
- {
- public override bool CanDrawTypeFilter(Type type)
- {
- return type != typeof(string);
- }
-
- protected override void DrawPropertyLayout(GUIContent label)
- {
- // Draw property here.
- }
- }
-
-
-
- [AllowGUIEnabledForReadonly]
- public sealed class SomeDrawerDrawer<T> : OdinValueDrawer<T> where T : class
- {
- }
-
-
- public class MyCustomBaseType
- {
-
- }
-
- public class MyCustomType : MyCustomBaseType
- {
-
- }
-
- // Remember to wrap your custom attribute drawer within a #if UNITY_EDITOR condition, or locate the file inside an Editor folder.
-
- public sealed class MyCustomBaseTypeDrawer<T> : OdinValueDrawer<T> where T : MyCustomBaseType
- {
- protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
- {
- T value = entry.SmartValue;
- // Draw your custom drawer here using GUILayout and EditorGUILAyout.
- }
- }
-
- // Usage:
- // Both values will be drawn using the MyCustomBaseTypeDrawer
- public class MyComponent : SerializedMonoBehaviour
- {
- public MyCustomBaseType A;
-
- public MyCustomType B;
- }
-
-
- // [OdinDrawer(OdinDrawerBehaviour.DrawProperty)] // default
- // [OdinDrawer(OdinDrawerBehaviour.AppendDecorator)]
- [OdinDrawer(OdinDrawerBehaviour.PrependDecorator)]
- [DrawerPriority(DrawerPriorityLevel.AttributePriority)]
- public sealed class MyCustomTypeDrawer<T> : OdinValueDrawer<T> where T : MyCustomType
- {
- public override bool CanDrawTypeFilter(Type type)
- {
- return type != typeof(SomeType);
- }
-
- protected override void DrawPropertyLayout(IPropertyValueEntry<T> entry, GUIContent label)
- {
- T value = entry.SmartValue;
- // Draw property here.
- }
- }
-
-
+ public class SomeWindow : OdinEditorWindow
+ {
+ [MenuItem("My Game/Some Window")]
+ private static void OpenWindow()
+ {
+ GetWindow<SomeWindow>().Show();
+ }
+
+ [Button(ButtonSizes.Large)]
+ public void SomeButton() { }
+
+ [TableList]
+ public SomeType[] SomeTableData;
+ }
+
+
+ public class DrawSomeSingletonInAnEditorWindow : OdinEditorWindow
+ {
+ [MenuItem("My Game/Some Window")]
+ private static void OpenWindow()
+ {
+ GetWindow<DrawSomeSingletonInAnEditorWindow>().Show();
+ }
+
+ protected override object GetTarget()
+ {
+ return MySingleton.Instance;
+ }
+ }
+
+
+ private void InspectObjectInWindow()
+ {
+ OdinEditorWindow.InspectObject(someObject);
+ }
+
+ private void InspectObjectInDropDownWithAutoHeight()
+ {
+ var btnRect = GUIHelper.GetCurrentLayoutRect();
+ OdinEditorWindow.InspectObjectInDropDown(someObject, btnRect, btnRect.width);
+ }
+
+ private void InspectObjectInDropDown()
+ {
+ var btnRect = GUIHelper.GetCurrentLayoutRect();
+ OdinEditorWindow.InspectObjectInDropDown(someObject, btnRect, new Vector2(btnRect.width, 100));
+ }
+
+ private void InspectObjectInACenteredWindow()
+ {
+ var window = OdinEditorWindow.InspectObject(someObject);
+ window.position = GUIHelper.GetEditorWindowRect().AlignCenter(270, 200);
+ }
+
+ private void OtherStuffYouCanDo()
+ {
+ var window = OdinEditorWindow.InspectObject(this.someObject);
+
+ window.position = GUIHelper.GetEditorWindowRect().AlignCenter(270, 200);
+ window.titleContent = new GUIContent("Custom title", EditorIcons.RulerRect.Active);
+ window.OnClose += () => Debug.Log("Window Closed");
+ window.OnBeginGUI += () => GUILayout.Label("-----------");
+ window.OnEndGUI += () => GUILayout.Label("-----------");
+ }
+
+
+ public class OdinMenuEditorWindowExample : OdinMenuEditorWindow
+ {
+ [SerializeField, HideLabel]
+ private SomeData someData = new SomeData();
+
+ protected override OdinMenuTree BuildMenuTree()
+ {
+ OdinMenuTree tree = new OdinMenuTree(supportsMultiSelect: true)
+ {
+ { "Home", this, EditorIcons.House }, // draws the someDataField in this case.
+ { "Odin Settings", null, SdfIconType.GearFill },
+ { "Odin Settings/Color Palettes", ColorPaletteManager.Instance, EditorIcons.EyeDropper },
+ { "Odin Settings/AOT Generation", AOTGenerationConfig.Instance, EditorIcons.SmartPhone },
+ { "Camera current", Camera.current },
+ { "Some Class", this.someData }
+ };
+
+ tree.AddAllAssetsAtPath("More Odin Settings", SirenixAssetPaths.OdinEditorConfigsPath, typeof(ScriptableObject), true)
+ .AddThumbnailIcons();
+
+ tree.AddAssetAtPath("Odin Getting Started", SirenixAssetPaths.SirenixPluginPath + "Getting Started With Odin.asset");
+
+ var customMenuItem = new OdinMenuItem(tree, "Menu Style", tree.DefaultMenuStyle);
+ tree.MenuItems.Insert(2, customMenuItem);
+
+ tree.Add("Menu/Items/Are/Created/As/Needed", new GUIContent());
+ tree.Add("Menu/Items/Are/Created", new GUIContent("And can be overridden"));
+
+ // As you can see, Odin provides a few ways to quickly add editors / objects to your menu tree.
+ // The API also gives you full control over the selection, etc..
+ // Make sure to check out the API Documentation for OdinMenuEditorWindow, OdinMenuTree and OdinMenuItem for more information on what you can do!
+
+ return tree;
+ }
+ }
+
+
+ OdinMenuTree tree = new OdinMenuTree(supportsMultiSelect: true)
+ {
+ { "Home", this, EditorIcons.House },
+ { "Odin Settings", null, SdfIconType.GearFill },
+ { "Odin Settings/Color Palettes", ColorPaletteManager.Instance, EditorIcons.EyeDropper },
+ { "Odin Settings/AOT Generation", AOTGenerationConfig.Instance, EditorIcons.SmartPhone },
+ { "Camera current", Camera.current },
+ { "Some Class", this.someData }
+ };
+
+ tree.AddAllAssetsAtPath("Some Menu Item", "Some Asset Path", typeof(ScriptableObject), true)
+ .AddThumbnailIcons();
+
+ tree.AddAssetAtPath("Some Second Menu Item", "SomeAssetPath/SomeAssetFile.asset");
+
+ var customMenuItem = new OdinMenuItem(tree, "Menu Style", tree.DefaultMenuStyle);
+ tree.MenuItems.Insert(2, customMenuItem);
+
+ tree.Add("Menu/Items/Are/Created/As/Needed", new GUIContent());
+ tree.Add("Menu/Items/Are/Created", new GUIContent("And can be overridden"));
+
+ OdinMenuTrees are typically used with
+ // Draw stuff
+ someTree.DrawMenuTree();
+ // Draw stuff
+ someTree.HandleKeybaordMenuNavigation();
+
+
+ OdinMenuTree tree = new OdinMenuTree();
+ tree.AddAllAssetsAtPath("Some Menu Item", "Some Asset Path", typeof(ScriptableObject), true)
+ .AddThumbnailIcons();
+ tree.AddAssetAtPath("Some Second Menu Item", "SomeAssetPath/SomeAssetFile.asset");
+ // etc...
+
+
+ KeyCode someEnumValue;
+
+ [OnInspectorGUI]
+ void OnInspectorGUI()
+ {
+ // Use the selector manually. See the documentation for OdinSelector for more information.
+ if (GUILayout.Button("Open Enum Selector"))
+ {
+ EnumSelector<KeyCode> selector = new EnumSelector<KeyCode>();
+ selector.SetSelection(this.someEnumValue);
+ selector.SelectionConfirmed += selection => this.someEnumValue = selection.FirstOrDefault();
+ selector.ShowInPopup(); // Returns the Odin Editor Window instance, in case you want to mess around with that as well.
+ }
+
+ // Draw an enum dropdown field which uses the EnumSelector popup:
+ this.someEnumValue = EnumSelector<KeyCode>.DrawEnumField(new GUIContent("My Label"), this.someEnumValue);
+ }
+
+ // All Odin Selectors can be rendered anywhere with Odin. This includes the EnumSelector.
+ EnumSelector<KeyCode> inlineSelector;
+
+ [ShowInInspector]
+ EnumSelector<KeyCode> InlineSelector
+ {
+ get { return this.inlineSelector ?? (this.inlineSelector = new EnumSelector<KeyCode>()); }
+ set { }
+ }
+
+
+ SomeType someValue;
+
+ [OnInspectorGUI]
+ void OnInspectorGUI()
+ {
+ if (GUILayout.Button("Open Generic Selector Popup"))
+ {
+ List<SomeType> source = ...;
+ GenericSelector<SomeType> selector = new GenericSelector<SomeType>("Title", false, x => x.Path, source);
+ selector.SetSelection(this.someValue);
+ selector.SelectionTree.Config.DrawSearchToolbar = false;
+ selector.SelectionTree.DefaultMenuStyle.Height = 22;
+ selector.SelectionConfirmed += selection => this.someValue = selection.FirstOrDefault()
+ var window = selector.ShowInPopup();
+ window.OnEndGUI += () => { EditorGUILayout.HelpBox("A quick way of injecting custom GUI to the editor window popup instance.", MessageType.Info); };
+ window.OnClose += selector.SelectionTree.Selection.ConfirmSelection; // Confirm selection when window clses.
+ }
+ }
+
+
+ public class MySelector : OdinSelector<SomeType>
+ {
+ private readonly List<SomeType> source;
+ private readonly bool supportsMultiSelect;
+
+ public MySelector(List<SomeType> source, bool supportsMultiSelect)
+ {
+ this.source = source;
+ this.supportsMultiSelect = supportsMultiSelect;
+ }
+
+ protected override void BuildSelectionTree(OdinMenuTree tree)
+ {
+ tree.Config.DrawSearchToolbar = true;
+ tree.Selection.SupportsMultiSelect = this.supportsMultiSelect;
+
+ tree.Add("Defaults/None", null);
+ tree.Add("Defaults/A", new SomeType());
+ tree.Add("Defaults/B", new SomeType());
+
+ tree.AddRange(this.source, x => x.Path, x => x.SomeTexture);
+ }
+
+ [OnInspectorGUI]
+ private void DrawInfoAboutSelectedItem()
+ {
+ SomeType selected = this.GetCurrentSelection().FirstOrDefault();
+
+ if (selected != null)
+ {
+ GUILayout.Label("Name: " + selected.Name);
+ GUILayout.Label("Data: " + selected.Data);
+ }
+ }
+ }
+
+ Usage:
+
+ void OnGUI()
+ {
+ if (GUILayout.Button("Open My Selector"))
+ {
+ List<SomeType> source = this.GetListOfThingsToSelectFrom();
+ MySelector selector = new MySelector(source, false);
+
+ selector.SetSelection(this.someValue);
+
+ selector.SelectionCancelled += () => { }; // Occurs when the popup window is closed, and no slection was confirmed.
+ selector.SelectionChanged += col => { };
+ selector.SelectionConfirmed += col => this.someValue = col.FirstOrDefault();
+
+ selector.ShowInPopup(); // Returns the Odin Editor Window instance, in case you want to mess around with that as well.
+ }
+ }
+
+ // All Odin Selectors can be rendered anywhere with Odin.
+ [ShowInInspector]
+ MySelector inlineSelector;
+
+
- private GUITable table;
-
- private void Init()
- {
- bool[,] boolArr = new bool[20,20];
-
- this.table = GUITable.Create(
- twoDimArray: boolArr,
- drawElement: (rect, x, y) => boolArr[x, y] = EditorGUI.Toggle(rect, boolArr[x, y]),
- horizontalLabel: "Optional Horizontal Label", // horizontalLabel is optional and can be null.
- columnLabels: (rect, x) => GUI.Label(rect, x.ToString()), // columnLabels is optional and can be null.
- verticalLabel: "Optional Vertical Label", // verticalLabel is optional and can be null.
- rowLabels: (rect, x) => GUI.Label(rect, x.ToString()) // rowLabels is optional and can be null.
- );
- }
-
- private void OnGUI()
- {
- this.table.DrawTable();
- }
-
-
- private GUITable table;
-
- private void Init()
- {
- Listt<SomeClasst> someList = new List<SomeClass>() { new SomeClass(), new SomeClass(), new SomeClass() };
-
- this.table = GUITable.Create(someList, "Optional Title",
- new GUITableColumn()
- {
- ColumnTitle = "A",
- OnGUI = (rect, i) => someList[i].A = EditorGUI.TextField(rect, someList[i].A),
- Width = 200,
- MinWidth = 100,
- },
- new GUITableColumn()
- {
- ColumnTitle = "B",
- OnGUI = (rect, i) => someList[i].B = EditorGUI.IntField(rect, someList[i].B),
- Resizable = false,
- },
- new GUITableColumn()
- {
- ColumnTitle = "C",
- OnGUI = (rect, i) => someList[i].C = EditorGUI.IntField(rect, someList[i].C),
- SpanColumnTitle = true,
- }
- );
- }
-
- private void OnGUI()
- {
- this.table.DrawTable();
- }
-
- private class SomeClass
- {
- public string A;
- public int B;
- public int C;
- public int D;
- }
-
-
- guiTable[x,y].GUIStyle += rect => EditorGUI.DrawRect(rect, Color.red);
-
-
- // Span horizontally:
- guiTable[x - 2,y] = null;
- guiTable[x - 1,y] = null;
- guiTable[x,y].SpanX = true;
- guiTable[x + 1,y] = null;
-
- // Span vertically:
- guiTable[x,y - 2] = null;
- guiTable[x,y - 1] = null;
- guiTable[x,y].SpanY = true;
- guiTable[x,y + 1] = null;
-
-
- var tabGroup = SirenixEditorGUI.CreateAnimatedTabGroup(someKey);
- // Register your tabs before starting BeginGroup.
- var tab1 = tabGroup.RegisterTab("tab 1");
- var tab2 = tabGroup.RegisterTab("tab 2");
-
- tabGroup.BeginGroup(drawToolbar: true);
- {
- if (tab1.BeginPage())
- {
- // Draw GUI for the first tab page;
- }
- tab1.EndPage();
-
- if (tab2.BeginPage())
- {
- // Draw GUI for the second tab page;
- }
- tab2.EndPage();
- }
- tabGroup.EndGroup();
-
- // Control the animation speed.
- tabGroup.AnimationSpeed = 0.2f;
-
- // If true, the tab group will have the height equal to the biggest page. Otherwise the tab group will animate in height as well when changing page.
- tabGroup.FixedHeight = true;
-
- // You can change page by calling:
- tabGroup.GoToNextPage();
- tabGroup.GoToPreviousPage();
-
-
+ var tabGroup = SirenixEditorGUI.CreateAnimatedTabGroup(someKey);
+ // Register your tabs before starting BeginGroup.
+ var tab1 = tabGroup.RegisterTab("tab 1");
+ var tab2 = tabGroup.RegisterTab("tab 2");
+
+ tabGroup.BeginGroup(drawToolbar: true);
+ {
+ if (tab1.BeginPage())
+ {
+ // Draw GUI for the first tab page;
+ }
+ tab1.EndPage();
+
+ if (tab2.BeginPage())
+ {
+ // Draw GUI for the second tab page;
+ }
+ tab2.EndPage();
+ }
+ tabGroup.EndGroup();
+
+ // Control the animation speed.
+ tabGroup.AnimationSpeed = 0.2f;
+
+ // If true, the tab group will have the height equal to the biggest page. Otherwise the tab group will animate in height as well when changing page.
+ tabGroup.FixedHeight = true;
+
+ // You can change page by calling:
+ tabGroup.GoToNextPage();
+ tabGroup.GoToPreviousPage();
+
+
+ private GUITable table;
+
+ private void Init()
+ {
+ bool[,] boolArr = new bool[20,20];
+
+ this.table = GUITable.Create(
+ twoDimArray: boolArr,
+ drawElement: (rect, x, y) => boolArr[x, y] = EditorGUI.Toggle(rect, boolArr[x, y]),
+ horizontalLabel: "Optional Horizontal Label", // horizontalLabel is optional and can be null.
+ columnLabels: (rect, x) => GUI.Label(rect, x.ToString()), // columnLabels is optional and can be null.
+ verticalLabel: "Optional Vertical Label", // verticalLabel is optional and can be null.
+ rowLabels: (rect, x) => GUI.Label(rect, x.ToString()) // rowLabels is optional and can be null.
+ );
+ }
+
+ private void OnGUI()
+ {
+ this.table.DrawTable();
+ }
+
+
+ private GUITable table;
+
+ private void Init()
+ {
+ Listt<SomeClasst> someList = new List<SomeClass>() { new SomeClass(), new SomeClass(), new SomeClass() };
+
+ this.table = GUITable.Create(someList, "Optional Title",
+ new GUITableColumn()
+ {
+ ColumnTitle = "A",
+ OnGUI = (rect, i) => someList[i].A = EditorGUI.TextField(rect, someList[i].A),
+ Width = 200,
+ MinWidth = 100,
+ },
+ new GUITableColumn()
+ {
+ ColumnTitle = "B",
+ OnGUI = (rect, i) => someList[i].B = EditorGUI.IntField(rect, someList[i].B),
+ Resizable = false,
+ },
+ new GUITableColumn()
+ {
+ ColumnTitle = "C",
+ OnGUI = (rect, i) => someList[i].C = EditorGUI.IntField(rect, someList[i].C),
+ SpanColumnTitle = true,
+ }
+ );
+ }
+
+ private void OnGUI()
+ {
+ this.table.DrawTable();
+ }
+
+ private class SomeClass
+ {
+ public string A;
+ public int B;
+ public int C;
+ public int D;
+ }
+
+
+ guiTable[x,y].GUIStyle += rect => EditorGUI.DrawRect(rect, Color.red);
+
+
+ // Span horizontally:
+ guiTable[x - 2,y] = null;
+ guiTable[x - 1,y] = null;
+ guiTable[x,y].SpanX = true;
+ guiTable[x + 1,y] = null;
+
+ // Span vertically:
+ guiTable[x,y - 2] = null;
+ guiTable[x,y - 1] = null;
+ guiTable[x,y].SpanY = true;
+ guiTable[x,y + 1] = null;
+
+
+ decimal meters = 5m;
+ decimal centimeters = ConvertUnitsFromTo(meters, Units.Meter, Units.Centimeter);
+ // centimeters = 500
+
+
+ decimal meters = 5m;
+ decimal centimeters = ConvertUnitsFromTo(meters, meterUnitInfo, centimeterUnitInfo);
+ // centimeters = 500
+
+
+ decimal meters = 5m;
+ if (TryConvertUnitsFromTo(meters, Units.Meter, Units.Centimeter, out decimal centimeters)
+ {
+ // centimeters = 500
+ }
+
+
+ decimal meters = 5m;
+ if (TryConvertUnitsFromTo(meters, meterUnitInfo, centimeterUnitInfo, out decimal centimeters))
+ {
+ // centimeters = 500
+ }
+
+
+ UnitNumberUtility.AddCustomUnit("Centimeter", new string[]{ "cm" }, "Distance", 100m);
+
+
+ UnitNumberUtility.AddCustomUnit("Centimeter", new string[]{ "cm" }, UnitCategory.Distance, 100m);
+
+
+ UnitNumberUtility.AddCustomUnit("Centimeter", new string[]{ "cm" }, "Distance", x => x / 100m, x = > x * 100m);
+
+
+ UnitNumberUtility.AddCustomUnit("Centimeter", new string[]{ "cm" }, UnitCategory.Distance, x => x / 100m, x = > x * 100m);
+
+
+ /// [DisallowAddressableSubAssetField]
+ /// public AssetReference Reference;
+ ///
+ ///
+ /// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(InlineEditorAttribute))]
+ /// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(PreviewFieldAttribute))]
+ ///
+ ///