- Home /
How can PropertyDrawer with fouldouts to draw at the appropriate height?
I'm trying to create a PropertyDrawer with a foldout. I've got something that mostly works, but there's a slight bug. What's happening is that the height is calculated correctly for the number of objects and whether it's expanded, but when the foldout gets changed in OnGUI, the PropertyHeight has already been calculated by GetPropertyHeight— so it's not drawn accurately until the next event that causes a redraw (click, hover, etc.)
⠀
Here's an example of what I'm talking about:
⠀
How can I get inspectors that use this PropertyDrawer to draw at the appropriate height without lagging behind by a redraw event?
⠀
It seems to me that what I want to do is just trigger a redraw (mark something as dirty? call repaint directly?) from my OnGUI, but I don't know what I'm looking for to do that. I found a suggestion elsewhere to call Editor.Repaint(), but I don't seem to have an Editor available to call that on.
⠀
Alternatively: I'm doing everything wrong and there's an easier way. (Oh, as a caveat, I don't think I can use EditorGUILayout here because of some complexities that I've removed from this simple case)
⠀
Here's the gist of the code:
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassPropertyDrawer : PropertyDrawer
{
// ... other functions
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect foldRect = MyCalcFoldRect(position, label);
EditorGUI.BeginProperty(position, label, property);
property.isExpanded = EditorGUI.Foldout(foldRect, property.isExpanded, label);
if (property.isExpanded)
{
bool wasEnabled = GUI.enabled;
GUI.enabled = false;
EditorGUI.indentLevel++;
var _relatedObjs = MyGetObjects(property);
Rect rect = MyInitListRect(position);
foreach (var gob in _currentTargetObjects)
{
rect = MyUpdateListRect(rect);
EditorGUI.ObjectField(rect, gob, typeof(GameObject), allowSceneObjects: true);
}
GUI.enabled = wasEnabled;
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (property.isExpanded)
{
return EditorGUIUtility.singleLineHeight + // the foldout
(MyGetObjects(property).Length) * // for each related obj:
(EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
}
else
{
return EditorGUIUtility.singleLineHeight;
}
}
}
Answer by mandisaw · Nov 23, 2020 at 12:04 AM
Just a heads-up for anyone who comes along later, EditorGUI.BeginFoldoutHeaderGroup
(and EditorGUI.EndFoldoutHeaderGroup
) will accomplish the same effect, including handling any internal SetDirty calls.
The style is slightly different (Header/Bold instead of normal label text), but that can be overridden with GUIStyle objects as desired.
Cheers!
Answer by silvematt · Mar 09, 2019 at 02:43 AM
After
EditorGUI.EndProperty();
Write:
EditorUtility.SetDirty(target);
This will force the Repaint.
I'm afraid I don't understand. I don't have an object called target: this is in a PropertyDrawer, not a Custom Editor.
The function expects a UnityEngine.Object, but I just have the SerializedProperty, which is one of the rare Unity classes that doesn't descend from that.
Answer by vtclayton · Mar 10, 2019 at 01:07 AM
Thanks, silvematt, for putting me on the right path with EditorUtility.SetDirty
. I just had to figure out how to get at the target object:
⠀
EditorUtility.SetDirty(property.serializedObject.targetObject);
⠀
did the trick to force the repaint.
⠀
(I also used EditorGUI.EndChangeCheck
to only force the repaint when needed.)