Strange resolution to editor error: explanation requested

I wrote a simple (abstract) PropertyDrawrer class for ScriptableObjects.
The following version of the PropertyDrawrer simply draws the ScriptableObject as BOTH an Editor “ObjectField”, followed by the internal members of the ScriptableObject itself.

Abstract PropertyDrawer Class:

[CustomPropertyDrawer(typeof(ScriptableObject))]
    public abstract class ScriptableObjectPropertyDrawer : PropertyDrawer 
    {
        Editor editorWindow = null;
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUI.BeginProperty(position, label, property);
            EditorGUI.ObjectField(position, property, this.fieldInfo.FieldType, label);
            Editor.CreateCachedEditor(property.objectReferenceValue, null, ref editorWindow);
            editorWindow.DrawDefaultInspector();
            EditorGUI.EndProperty();
        }
    }

Sample ScriptableObject class: Settings

public class Settings : ScriptableObject {
    public int intValue;
    public string stringValue;}

Sample Monobehavior that CONTAINS the sample scriptable object: MonoTest

public class MonoTest : MonoBehaviour {
    public Settings s; }

Explicit definition of the PropertyDrawer for our Settings scriptable object. This tells the system to use our custom property drawer for ALL “Settings”.

[CustomPropertyDrawer(typeof(Settings))]
public class SettingsPropertyDrawer : ScriptableObjectPropertyDrawer {}

Now as far as I understand things, this SHOULD be all that is necessary. Default inspectors should now use the newly defined SettingsPropertyDrawer , when drawing any “Settings”. However, compiling this code, and then selecting/inspecting a MonoTest object in the Editor, yields the following error:

ArgumentException: Getting control 9’s position in a group with only 9 controls when doing Repaint
Aborting

The call Stack shows that the line in MY code generating the error is the line…

editorWindow.DrawDefaultInspector();

Though the actual error is generated by internal unity code:

UnityEngine.GUILayoutGroup.GetNext () (at C:/buildslave/unity/build/Runtime/IMGUI/Managed/GUILayoutUtility.cs:637)

Now, I HAVE seen a bunch of posts on this error, but they all have to do with: changing what controls are being drawn, at the wrong time.
Since I have no IF statements in my code, it’s pretty obvious that changing controls is NOT the issue.

RESOLUTION: Oddly, adding in THIS do-nothing class resolved the error.

[CustomEditor(typeof(MonoTest))]
    public class MonoTestEditor : Editor{}

Questions:

Why does this resolve it?

What can I do so that users of the PropertyDrawer class do NOT need to define a custom editor for their monobehavior, like the resolution above.

I doubt this is a unity bug, where am I making wrong assumptions?

I’m not sure if your still at this problem, but i guess i know the reason for this error ^^. It’s a bit complicated. Like Audrius of the Unity QA staff said your messing with the layout system here but in a way that isn’t obvious at first glance.

First of all PropertyDrawers don’t use the layout system at all. That means the default inspector doesn’t propergate the Layout event down to the property drawers.

Next thing is the default inspector is something different from calling “DrawDefaultInspector” inside a custom editor. The built-in default editor uses an optimised GUI block which doesn’t use the layout system to gain performance. However if you implement a custom inspector it does use the layout event.

That’s why the Layout event is propergated to the propertydrawer because DrawDefaultInspector (which is in the virtual OnInspectorGUI method by default) does receive Layout events.

As said in the beginning property drawers don’t use and shouldn’t use the layout system. They have a predefined Rect which they should use to draw their stuff. If you need “more space” for your property you have to override the PropertyDrawer.GetPropertyHeight method which is invoked before your actual OnGUI method is called. The Rect your OnGUI method receives will have that height.

While implementing a custom editor for the containing class does solve the problem, you shouldn’t use the layout system in a PropertyDrawer in the first place. The layout system can’t “expand” the space of the property drawer since the size of one property is fix (based on GetPropertyHeight).

ps:
You shouldn’t have a CustomPropertyDrawer attribute on your abstract base class ScriptableObjectPropertyDrawer. “CustomPropertyDrawer” indicates the editor to instantiate that class for the given property type. Since an abstract class can never be created it makes no sense to add a CustomPropertyDrawer attribute.

edit

So here we are, as mentioned in the comment below here’s a bit of Unity’s internal way of handling classes that don’t have a custom inspector. These are just the two essential methods you should worry about:

// UnityEditor.Editor
internal bool GetOptimizedGUIBlockImplementation(bool isDirty, bool isVisible, out OptimizedGUIBlock block, out float height)
{
	if (this.m_OptimizedBlock == null)
	{
		this.m_OptimizedBlock = new OptimizedGUIBlock();
	}
	block = this.m_OptimizedBlock;
	if (!isVisible)
	{
		height = 0f;
		return true;
	}
	if (this.m_SerializedObject == null)
	{
		this.m_SerializedObject = new SerializedObject(this.targets);
	}
	else
	{
		this.m_SerializedObject.Update();
	}
	this.m_SerializedObject.inspectorMode = this.m_InspectorMode;
	SerializedProperty iterator = this.m_SerializedObject.GetIterator();
	height = 2f;
	bool enterChildren = true;
	while (iterator.NextVisible(enterChildren))
	{
		height += EditorGUI.GetPropertyHeight(iterator, null, true) + 2f;
		enterChildren = false;
	}
	if (height == 2f)
	{
		height = 0f;
	}
	return true;
}

and

// UnityEditor.Editor
internal bool OptimizedInspectorGUIImplementation(Rect contentRect)
{
	SerializedProperty iterator = this.m_SerializedObject.GetIterator();
	bool enterChildren = true;
	bool enabled = GUI.enabled;
	contentRect.xMin += 14f;
	contentRect.xMax -= 4f;
	contentRect.y += 2f;
	while (iterator.NextVisible(enterChildren))
	{
		contentRect.height = EditorGUI.GetPropertyHeight(iterator, null, false);
		EditorGUI.indentLevel = iterator.depth;
		using (new EditorGUI.DisabledGroupScope(this.m_InspectorMode == InspectorMode.Normal && "m_Script" == iterator.propertyPath))
		{
			enterChildren = EditorGUI.PropertyField(contentRect, iterator);
		}
		contentRect.y += contentRect.height + 2f;
	}
	GUI.enabled = enabled;
	return this.m_SerializedObject.ApplyModifiedProperties();
}

“GetOptimizedGUIBlockImplementation” basically only iterates through all properties and (pre-) calculates the required height. The InspectorWindow will now reserve a Rect with with that size using the Layout system. The resulting rect is then finally passed to “OptimizedInspectorGUIImplementation”. Here they left out the layout event on purpose since PropertyDrawers must not use the layoutsystem and the size is known beforehand. Note: Unity does some optimisations to avoid recalculating the size each redraw. That’s what the internal class “OptimizedGUIBlock” is good for. It caches the relevant information on the native C++ side of the editor. Again, this code only executes when there’s no custom inspector for that class. Otherwise the custom inspector will draw the properties.

So all you have to do is cutting out the relevant part of “GetOptimizedGUIBlockImplementation” which calculates the required height and put it in your GetPropertyHeight method of your PropertyDrawer and cut out the relevant part of “OptimizedInspectorGUIImplementation” which actually draws the properties inside your property Rect (which should have the right height now).