- Home /
PropertyDrawer, Change property within callback
I write a custom property drawer for Enum type. I need to draw categorized dropdown menu instead of regular enum dropdown (using GenericMenu type).
GenericMenu uses callback function to apply menu item selection. Instead of regular function I use lambda expression to keep current scope (and to use current property reference). The problem is, that I change property.intValue to set newly selected enum, but this changes is not saved.
My code:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
//ItemType is referenced enum
var curretnType = (ItemType)property.intValue;
if (GUI.Button(position, curretnType.ToString(), EditorStyles.toolbarButton))
{
ItemEntry[] entries = ... // basically represent all enum entries of ItemType enum with some extra data
GenericMenu toolsMenu = new GenericMenu();
foreach (ItemEntry entry in entries)
{
string path = ...
ItemType entrtType = entry.Type;
toolsMenu.AddItem(new GUIContent(path), false, () =>
{
property.intValue = (int)entrtType;
Debug.Log(property.intValue);
});
}
toolsMenu.ShowAsContext();
}
EditorGUI.EndProperty();
}
Debug.Log shows, that correct value is setted, but this "curretnType" I use as a lable for a button is not chnaging.
How can I cache current property to change it's values outside OnGUI method?
Thanks!
Answer by Deadcow_ · Jul 07, 2015 at 04:10 PM
Well, I don't know what caused this strange issue (and hope I'll find explanation eventually), but to make this work now I use private variable
private int _newValue = -1;
in the callback function I set it to new enum value
toolsMenu.AddItem(new GUIContent(path), false,
() => _newValue = (int)entryType);
and at the end of OnGUI method:
if (_newValue > 0)
{
property.intValue = _newValue;
_newValue = -1;
}
My Enum starts from 1 so it works fine
You need some refactoring ^^:
curretnType
entrtType
On topic: I barely used the generic menu, but it looks like the item callback is fired from beyond the OnGUI callback. You can verify that by placing a Debug.Log right after your "tools$$anonymous$$enu.ShowAsContext();" call. You will notice that this Log will show before your item callback. That means when using "ShowAsContext()" Unity seems to just "remember" that it should open that menu once the OnGUI callback is done. So the menu get scheduled to be displayed. So the inspector most likely calls Apply$$anonymous$$odifiedProperties before your item callback is fired.
I'm not sure if would help (or actually work) when you call:
property.serializedObject.Apply$$anonymous$$odifiedProperties();
inside your item callback once you changed it.
SerializedObject and SerializedProperty are actually wrapper objects around your actual data. When you call "Update" on the serializedObject it will read the actual values from the instance into the wrapper classes. When you call Apply$$anonymous$$odifiedProperties changes you've made to those wrapper objects are written back to the actual instance. Since the item callback happens after Apply$$anonymous$$odifiedProperties has already been fired, the changes are lost.
ps: your workaround probably applies the changes the next time OnGUI is called, then it sees the changed value and changes the property before Apply$$anonymous$$odifiedProperties is called.
Thanks a lot! It's pretty first time I dabble with SerializedProperty type and I missed this serializableObject reference.
And yeah, na$$anonymous$$g is messed up, thanks for notice) It'll be surely much readable with your solution!
Answer by gameplay4all · Jul 07, 2015 at 02:41 PM
Hi there,
It's probably a problem with the script where you actually call this drawer: So you have a script with a line of code containing something like this:
EditorGUI.PropertyField( yourRect, yourSerializableObject.FindProperty(YourProperty));
Right? Before this line you need to call:
yourSerializableObject.Update();
And afterwards you need to call this:
yourSerializableObject.ApplyModifiedProperties();
So in total it looks like this:
yourSerializableObject.Update();
EditorGUI.PropertyField( yourRect, yourSerializableObject.FindProperty(YourProperty));
yourSerializableObject.ApplyModifiedProperties();
I hope this helps!
-Gijs
For now I tested this property drawer by simply putting this property in script. So it just defauld inspector for my script with
public ItemType Type = ItemType.None;
I force to set default value to None, but it shouldn't affect custom property drawer behaviour