I want to expose different elements in the inspector depending on the value of a bool. When writing a custom inspector I could just write if(true) display X, else display Y, but for some reason it does not work nicely for property drawers. The inspector only updates if I unselect, then reselect the object, it doesn’t redraw instantly when I change the value of the bool.
This is what my code looks like right now (simplified)
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
// Create a new VisualElement to be the root the property UI.
var container = new VisualElement();
var popup = new UnityEngine.UIElements.PopupWindow();
// Toggle using X or Y
if (property.FindPropertyRelative("useX").boolValue)
{
popup.Add(new PropertyField(property.FindPropertyRelative("X"), "X"));
}
else
{
popup.Add(new PropertyField(property.FindPropertyRelative("Y"), "Y"));
}
container.Add(popup);
return container;
}
I’ve searched around and it seems like it may be pretty complicated to do this with property drawers, does anyone know what specifically I need to do to achieve what I want?
UI toolkit isn’t like IMGUI that the code is constantly running. CreatePropertyGUI runs only once when the inspector is being generated. So instead of redirecting your logical flow, you need to design your UI to have some form of state that handles changes in your UI, usually through callbacks.
This is pretty easy to do as you can easily write custom visual elements to encapsulate the required functionality.
Very often I just define these in the drawer themselves as a nested class, like so:
public sealed class MyPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
return new MyVisualElement(property);
}
public sealed class MyVisualElement : VisualElement
{
public MyVisualElement(SerializedProperty property) : base()
{
_property = property;
// build visual elements
}
private readonly SerializedProperty _property;
}
}
Then in this custom visual element you can define all the state and hook into the needed callbacks. So you’d have a boolean field bound to a respective serialized property, register a value changed callback, then in said method show or hide the necessary visual elements.
It’s different but in the end it makes things easier to manage.
I’m still very confused as to what I can do here. I don’t understand the purpose of writing a custom visual element, how to even write one or a state for one, how a callback works and is written, and where I’d write those 
Feels like I’m in a totally alien part of Unity and C# I know absolutely nothing about!
If it isn’t too much trouble, would you be able to explain what this structure with callbacks might look like, or provide some resources where I can learn how to work it? I’ve been through the docs and I’ve seen other discussions that talk about callbacks for this use case but unfortunately I’m struggling to parse anything I’m reading
I can write an example later when I’m in front of a computer with Unity.
Though FWIW you can still write IMGUI property drawers by overriding the OnGUI method instead.
It seems like you’re used to writing IMGUI inspectors already so it should mostly be the same workflow with an IMGUI property drawer.
UI Toolkit is a completely different, newer UI workflow.
So while the real answer for you is probably to just override the OnGUI method in your property drawer instead, for yourself and everyone else, here’s a simple example with UI toolkit:
using UnityEngine;
using UnityEngine.UIElements;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UIElements;
#endif
public class TestScriptableObject : ScriptableObject
{
public SomeClass SomeClass;
}
[System.Serializable]
public class SomeClass
{
public bool Toggle;
public int ValueA;
public string ValueB;
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SomeClass))]
public sealed class SomeClassPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
return new SomeClassElement(property);
}
public sealed class SomeClassElement : VisualElement
{
#region Constructors
public SomeClassElement(SerializedProperty property) : base()
{
var toggleProperty = property.FindPropertyRelative("Toggle");
var valueAProperty = property.FindPropertyRelative("ValueA");
var valueBProperty = property.FindPropertyRelative("ValueB");
var toggleField = new Toggle(toggleProperty.displayName);
toggleField.BindProperty(toggleProperty);
toggleField.RegisterValueChangedCallback<bool>(HandleToggleValueChanged);
Add(toggleField);
_valueAField = new PropertyField(valueAProperty);
Add(_valueAField);
_valueBField = new PropertyField(valueBProperty);
Add(_valueBField);
bool toggle = toggleProperty.boolValue;
UpdateToggleVisibility(toggle);
}
#endregion
#region Internal Members
private readonly PropertyField _valueAField;
private readonly PropertyField _valueBField;
#endregion
#region Internal Methods
private void HandleToggleValueChanged(ChangeEvent<bool> changeEvent)
{
bool toggle = changeEvent.newValue;
UpdateToggleVisibility(toggle);
}
private void UpdateToggleVisibility(bool toggle)
{
_valueAField.style.display = toggle ? DisplayStyle.Flex : DisplayStyle.None;
_valueBField.style.display = toggle ? DisplayStyle.None : DisplayStyle.Flex;
}
#endregion
}
}
#endif
2 Likes
Thank you! I might do it with OnGui later, but I wanted to at least try UI toolkit cuz Unity wants to push it over IMGUI, plus it’s way easier to make things looks nice with UI toolkit imo
1 Like