How to show the default inspector of a struct in a custom editor window?

Hi, I have a struct and I want the user to be able to change the values of it in a custom window.

How can I do this?

I believe I’ve told you this before: a PropertyField (either UI Toolkit or IMGUI) is used to draw the default inspector for a serialized property.

If this is in an EditorWindow you can serialize the field in into the editor window (it inherits from scriptable object), make a SerializedObject from the window, get the SerializedProperty for your field and then draw it.

1 Like

Yes, I remember talking to you about that for another thing.

But I did not know that I could use the window to get it. Do I have to create a field for each variable of my struct or is only one propertyfield needed to display the struct directly?

Drawing a property via its serialized property will also draw its children.

Example:

using UnityEngine;
using UnityEditor;
using UnityEditor.UIElements;

public sealed class EditorWindowExample : EditorWindow
{
	#region Window Opening

	[MenuItem("Testing/Editor Window Example")]
	private static void Open()
	{
		GetWindow<EditorWindowExample>().Show();
	}

	#endregion

	#region Internal Members

	[SerializeField]
	private ExampleStruct _exampleStruct;

	private SerializedObject _serializedObject;

	#endregion

	#region Methods

	private void OnEnable()
	{
		_serializedObject = new SerializedObject(this);
	}

	private void OnDestroy()
	{
		_serializedObject.Dispose();
	}

	private void CreateGUI()
	{
		var root = this.rootVisualElement;
		var property = _serializedObject.FindProperty(nameof(_exampleStruct));
		var propertyField = new PropertyField(property, "Example");
		propertyField.Bind(_serializedObject);
		root.Add(propertyField);
	}

	#endregion

	#region Nested Types

	[System.Serializable]
	public struct ExampleStruct
	{
		public string Name;

		public string Description;
	}

	#endregion
}

Result:

1 Like

But I have to make the field serialisable even in the inspector! Ok, I understand now, because I did pretty much what you did, but nothing appeared.

Thanks a lot!

No, a single PropertyField is enough. Though it should be mentioned that a SerializedObject can only “look” at classes derived from UnityEngine.Object. This is not really an issue when you are inside an EditorWindow since an EditorWindow is a ScriptableObject. So when you have a serialized field in your EditorWindow class that holds your struct, you can use a SerializedObject on your EditorWindow instance and get a SerializedProperty of that serialized Field.

Keep in mind to call Update on the SerializedObject before drawing your SerializedProperty and to call ApplyModifedProperties after your have drawn the property.

Note that when using IMGUI, EditorGUILayout.PropertyField uses the layout system to reserve the necessary space in the UI system. When you don’t want to use the layout system, the EditorGUI.PropertyField requires you to use EditorGUI.GetPropertyHeight in order to adjust the height of the rect to ensure it’s large enough.

I don’t believe this is really required anymore with UI Toolkit. It’s binding system should handle this all automatically.

1 Like

Yes, you’re right. I was talking about IMGUI :slight_smile: I started writing the answer before yours was posted :wink:

1 Like

I have just changed and I am now using a scriptable object to store the data of the tab, I use a custom editor on this scriptable object to make it look as I want and the window will allow me to switch between 3 different tabs, each represented as a scriptable object.

I hope this is clear, but I don’t know if this is a good solution or if it is too complicated.

Also, I just noticed that if I use a RegisterValueChangeCallback on the struct PropertyField, it is not called when changing a value in my struct. Is this normal and if so how can I make it work in some way.

private void CreateGUI()
{
    var root = this.rootVisualElement;
    var property = _serializedObject.FindProperty(nameof(_exampleStruct));
    var propertyField = new PropertyField(property, "Example");
    propertyField.RegisterValueChangeCallback(e =>
    {
        Debug.Log("Updated"); // This is never call when I change a value of my struct
    });
    propertyField.Bind(_serializedObject);
    root.Add(propertyField);
}

I think that one will only trigger when you assign the value of the field itself. In the case of a property field bound to a struct: when you assign a new struct to it.

For editor binding, use the TrackPropertyValue extension method instead: Unity - Scripting API: UIElements.BindingExtensions.TrackPropertyValue

This gets me a callback whenever a child property is changed:

private void CreateGUI()
{
	var root = this.rootVisualElement;
	var property = _serializedObject.FindProperty(nameof(_exampleStruct));
	var propertyField = new PropertyField(property, "Example");
	propertyField.Bind(_serializedObject);
	propertyField.TrackPropertyValue(property, TestCallback);
	root.Add(propertyField);
}

private void TestCallback(SerializedProperty property)
{
	Debug.Log(property.name);
}

Note that it prints the name of the struct’s serialized property (_exampleStruct), and not the child property you change.

1 Like

It works perfectly, thank you!