This is a Tool that allows the auto generation of UXML & custom inspector scripts by using attributes
NOTE: Other versions of Unity need to be tested to expand this list.
- Unity Version 2021.3.12f1
- Use the
Package Manager -> ➕ -> Add package from git URL... - Use link
https://github.com/AlexBedardReidU3D/UIToolkitAttributes.git
- Add
[GenerateUXML]to anyMonobehaviouror Value type/ Reference Type - Allow the scripts to recompile, and 2 new auto-generated scripts will have been added to:
Assets/Editor/Custom Inspector/TYPE_NAME/
- ⚠ Implement auto-cleanup of deleted Types
- Stacked Groups utilizing Dynamic Labels
- Enum Flags Support
- Scriptable Objects Support
- Custom Label for
structutilizing[BoxGroup] - Implement
[HideLabel]to hide label - Implement
[LeftToggle] - Implement
[Required]to show warning when missing reference - Implement
[EnumToggleButtons] - Implement
[Tooltip] - Implement
[HideIf]/[ShowIf]
Table of Contents
- The Attributes
- Known Issues
- Change Log
Table of contents generated with markdown-toc
Links
Add this attribute to any serializable Class or Struct. Adding this to a Monobehaviour would
generate a CustomInspector, otherwise a PropertyDrawer will be created.
//Generate UXML Custom Inspector for Components
[GenerateUXML]
public class MyClass : MonoBehaviour
{
//Generate UXML Property Drawers for non-Components
[Serializable, GenerateUXML]
public struct MyStruct
{
}
}This will generate a button element, that will Invoke the function it sits above.
This will also work with the conditional attributes.
[Button]
private void MyMethod()
{
/*My Method Code*/
}[Button("My Custom Button Label")]
private void MyMethod()
{
/*My Method Code*/
}- Parameter: text
string- If this parameter is not included, then the name of the method will be used , then the name of the method will be used
- This optional parameter will set the text of the button to the string passed.
- If a string starts with the
$character ($m_myField) then it will attempt to get the string of the field name provided
Custom Labels allow for overrides on the field name used in the script. This name can be a constant string or a dynamic member reference.
[CustomLabel("My Custom Label")]
public int myValue;
[CustomLabel("$myDynamicLabel")]
public int myNewValue;
public string myDynamicLabel;- Parameter: text
string- Text to display as the fields label
- If a string starts with the
$character ($m_myField) then it will attempt to get the string of the field name provided
This displays the value & field name as an editable string (No Field Entry). This can be used
in conjunction with [CustomLabel()].
[DisplayAsString]
public int myValue;This will display a text box with a warning/info icon and whatever text is passed to the attribute. This will appear immediately over the field it is with. This will also appear within groups as expected.
[InfoBox("This is a warning")]
public int myValue;- Parameter: infoText
string- Text to display for the info box body
Unlike [DisplayAsString], [ReadOnly] only prevents the field from being edited in the
inspector, leaving the field visible.
[ReadOnly]
public int myValue;[BoxGroup(string)][FoldoutGroup(string)][HorizontalLayoutGroup(string)][TitleGroup(string)][VerticalLayoutGroup(string)]
- Groups can contain multiple elements.
- A group can exist as a sub group to another group.
- Some groups use a label which can either be specified
- The group name in the path will be used as the label if no label was specified.
- A group can be stacked on a single field, but each group must be declared before a sub group can be used.
- It does not have to be declared on the same value, but it must be declared above the field.
All groups have:
- Parameter: path
string- The group hierarchy, with each group separated by
/
- The group hierarchy, with each group separated by
Some Groups have:
- Optional Parameter: label
string- Text to display for the groups label
- If a string starts with the
$character ($m_myField) then it will attempt to get the string of the field name provided
//This will throw an exception
//-----------------------------------//
[BoxGroup("MyGroup/MySubGroup/MySubSubGroup")]
public int myValue;//This will not throw an exception
//-----------------------------------//
[VerticalLayoutGroup("MyGroup")] // <-- Declare MyGroup
[HorizontalLayoutGroup("MyGroup/MySubGroup")] // <-- Declare MySubGroup, as a child of MyGroup
[BoxGroup("MyGroup/MySubGroup/MySubSubGroup")] // <-- Declare MySubSubGroup, as a child of MySubGroup
public int myValue;//This will not throw an exception
//-----------------------------------//
[VerticalLayoutGroup("MyGroup")] // <-- Declare MyGroup
public int myValue;
[HorizontalLayoutGroup("MyGroup/MySubGroup")] // <-- Declare MySubGroup, as a child of MyGroup
public int myValue2;
[BoxGroup("MyGroup/MySubGroup/MySubSubGroup")] // <-- Declare MySubSubGroup, as a child of MySubGroup
public int myValue3;A declared group type must match all other references to it:
//This will throw an exception
//-----------------------------------//
[BoxGroup("MyGroup")]
public int myValue;
[FoldoutGroup("MyGroup")] // <-- This does not match BoxGroup with the same name specified above
public int myOtherValue;//This will not throw an exception
//-----------------------------------//
[BoxGroup("MyGroup")]
public int myValue;
[BoxGroup("MyGroup")]
public int myOtherValue;A static box with a label to contain multiple elements.
[BoxGroup("MyBoxGroup")]
public int myValue;
[BoxGroup("MyOtherBoxGroup", "My Custom Label")]
public int myOtherValue;A struct can also be display as a box group instead of the normal foldout by specifying the use of [BoxGroup].
NOTE: The struct must utilize
[GenerateUXML]for this to work.
[Serializable, GenerateUXML]
public struct MyStruct
{
public int myValue;
}
public MyStruct myDefaultDisplay;
[BoxGroup("myBoxDisplay")]
public MyStruct myBoxDisplay;Similar to [BoxGroup] this group uses a header to contain multiple elements, but with the addition of a toggle to fold
the group in or out.
[FoldoutGroup("MyGroup")]
public int myValue;
[FoldoutGroup("MyOtherGroup", "My Custom Label")]
public int myOtherValue;
[FoldoutGroup("MyDynamicGroup", "$dynamicLabel")]
public int myValue2;
public string dynamicLabel;Horizontal groups do not utilize a label, so act as almost an invisible group/container. Horizontal groups use the flex layout
setting row to stack the elements horizontally.
[HorizontalLayoutGroup("MyGroup")]
public int myValue;
[HorizontalLayoutGroup("MyGroup")]
public int myOtherValue;Title Groups are similar to the BoxGroup because they use a header, though instead of a container a bolded title with an underline exists instead.
[TitleGroup("MyGroup")]
public int myValue;
[TitleGroup("MyOtherGroup", "My Custom Label")]
public int myOtherValue;
[TitleGroup("MyDynamicGroup", "$dynamicLabel")]
public int myValue2;
public string dynamicLabel;Similar to the [HorizontalLayoutGroup] this is an invisible container with no title. However, this uses the default column
setting for flex layout.
[VerticalLayoutGroup("MyGroup")]
public int myValue;
[VerticalLayoutGroup("MyGroup")]
public int myOtherValue;Conditionals allow the locking of elements by enabling or disabling them depending on the conditions specified. When an element
is disabled, input will no longer be possible. Conditionals can also be applied to [Button] attributes.
This will disable the member when either the condition member returns true OR when the condition member matches the
included optional expectedValue.
//This uses the value returned by shouldToggle
//-----------------------------------//
public bool shouldToggle;
[DisableIf("shouldToggle")]
public int myValue;
//This will compare threshold to 10
//-----------------------------------//
public int threshold;
[DisableIf("threshold", 10)]
public int myOtherValue;public enum MyEnum
{
One,
Two,
Three
}
public MyEnum myEnum;
//This will compare myEnum to MyEnum.Two
//-----------------------------------//
[Button, DisableIf("myEnum", MyEnum.Two)]
public void MyButton()
{
//Method Body
}- Parameter: condition
string- The Member name to use when attempting to determine if this element should be enabled
- The Member should return a bool value (If not using the optional parameter)
- If using the optional parameter, the member can be anything that returns an
object
- Optional Parameter: expectedValue
object- This is a constant value in which to compare the specified member
This will enable the member when either the condition member returns true OR when the condition member matches the
included optional expectedValue.
//This uses the value returned by shouldToggle
//-----------------------------------//
public bool shouldToggle;
[EnableIf("shouldToggle")]
public int myValue;
//This will compare threshold to 10
//-----------------------------------//
public int threshold;
[EnableIf("threshold", 10)]
public int myOtherValue;public enum MyEnum
{
One,
Two,
Three
}
public MyEnum myEnum;
//This will compare myEnum to MyEnum.Two
//-----------------------------------//
[Button, EnableIf("myEnum", MyEnum.Two)]
public void MyButton()
{
//Method Body
}- Parameter: condition
string- The Member name to use when attempting to determine if this element should be enabled
- The Member should return a bool value (If not using the optional parameter)
- If using the optional parameter, the member can be anything that returns an
object
- Optional Parameter: expectedValue
object- This is a constant value in which to compare the specified member
This will apply the disabled functionality when out of play mode, and will switch when pressing play
[DisableInEditorMode]
public int myValue;This will apply the disabled functionality when in play mode, and will switch when entering back into editor
[DisableInPlayMode]
public int myValue;| Attribute | AttributeTargets | Allow Multiple |
|---|---|---|
[GenerateUXML] |
Class, Struct |
false |
[Button()] |
Method |
false |
[CustomLabel()] |
Field |
false |
[DisplayAsString] |
Field |
false |
[InfoBox()] |
Field, Method |
false |
[ReadOnly] |
Field |
false |
| Attribute | AttributeTargets | Allow Multiple |
|---|---|---|
[BoxGroup()] |
Field, Method |
true |
[FoldoutGroup()] |
Field, Method |
true |
[HorizontalLayoutGroup()] |
Field, Method |
true |
[TitleGroup()] |
Field, Method |
true |
[VerticalLayoutGroup()] |
Field, Method |
true |
| Attribute | AttributeTargets | Allow Multiple |
|---|---|---|
[DisableIf()] |
Field, Method |
false |
[EnableIf()] |
Field, Method |
false |
[DisableInEditorMode] |
Field, Method |
false |
[DisableInPlayMode] |
Field, Method |
false |
- When using
[HorizontalLayoutGroup]children that use a field, the sizing is off as the width must be fixed, but causes the field to be a strange size struct[BoxGroup]does not work as expected, causing the struct to remain a Foldout Group once inside the box group[Button]with a dynamic label does not appear to work as expected
- 0.0.1 package created