Arrays and Property Drawers - Weird position results

Because arrays in the default inspector are particularly cumbersome, I frequently find myself creating property drawers for them. Mainly, I’m removing the size field and using two buttons to add/remove an entry, and using another property drawer for the entries without drawing the usual ‘Element 0’, Element 1’, etc.

Today, however, I must be doing something horribly wrong because I’m having to change the rect positions in strange ways to get things to look ‘normal’.

In this case, I made a wrapper class that holds an array of a simple class with two fields. The idea is that the two fields will appear on the same line for each array entry, as shown:

The problem, is that in order to produce this result, the rect values are all out of wack and seem to make no sense.

Here is the wrapper property drawer. It simply draws the buttons, and then each line of the array.

[CustomPropertyDrawer (typeof (FS_Detector.EmissionWrapper))]
public class EmissionWrapper : PropertyDrawer {

    int lh1 = 18; // line height

    public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
        return lh1 + ( lh1 * property.FindPropertyRelative("emission").arraySize );
    }

    // Draw the property inside the given rect
    public override void OnGUI ( Rect position, SerializedProperty property, GUIContent label ) {
        EditorGUI.BeginProperty (position, label, property);

        SerializedProperty emissionProp = property.FindPropertyRelative("emission");

        Rect textR =    new Rect ( position.x,        position.y,      100f,   lh1 );
        Rect ctrlAdd =  new Rect ( position.x+70f,    position.y+1f,   20f,    lh1-6f );
        Rect ctrlRem =  new Rect ( position.x+93f,    position.y+1f,   20f,    lh1-6f );

        EditorGUI.LabelField( textR, "Emissions", EditorStyles.label );
        if ( GUI.Button( ctrlAdd, "+" ) ) {
            emissionProp.InsertArrayElementAtIndex( emissionProp.arraySize );
        }
        if ( GUI.Button( ctrlRem, "-" ) ) {
            emissionProp.DeleteArrayElementAtIndex( emissionProp.arraySize-1 );
        }
        for ( int i=0; i < emissionProp.arraySize; i++ ) {
            // draw every element of the array
            Rect valueR = new Rect (position.x,    position.y + lh1 + (lh1 * i),    position.width,   lh1 );
            EditorGUI.PropertyField( valueR, emissionProp.GetArrayElementAtIndex(i), GUIContent.none );
        }

        EditorGUI.EndProperty ();
    }
}

And here is the property drawer for each array entry:

[CustomPropertyDrawer (typeof (FS_Detector.EmissionBlock))]
public class EmissionBlock : PropertyDrawer {

    int lh1 = 18; // line height

    // Draw the property inside the given rect
    public override void OnGUI ( Rect position, SerializedProperty property, GUIContent label ) {
        int indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        Rect textR =      new Rect ( position.x+20f,    position.y,         100f,     lh1 );
        Rect typeR =      new Rect ( position.x-20f,    position.y,         220f,     lh1 );
        Rect text2R =     new Rect ( position.x+135f,   position.y,         100f,     lh1 );
        Rect strengthR =  new Rect ( position.x+45f,    position.y + lh1,   200f,     lh1 );

        EditorGUI.LabelField( textR, "Type", EditorStyles.label );
        EditorGUI.PropertyField( typeR, property.FindPropertyRelative("type"), GUIContent.none );
        EditorGUI.LabelField( text2R, "Strength", EditorStyles.label );
        EditorGUI.PropertyField( strengthR, property.FindPropertyRelative("strength"), GUIContent.none );

        EditorGUI.indentLevel = indent;
    }
}

The problem lines seem to be the rects on lines 11-14.

Compare the rect x-values with where they end up in the picture. For example, according to the code, it looks like typeR (line 12) would be positioned before textR (line 11) based onx location, yet it isn’t.

Also of particular note, is the rect on line 14. The y-value is position.y + lh1, unlike the others (lh1 drops it down a line) yet they all end up on the same line in the pic.

Additionally, changing only the width of these rects also has the side-effect of changing it’s own x location. Somehow.

Any insight as to what’s happening here?

Would this be better off posted in a different forum?

Why dont you use GUILayout rather than GUI

This would be an example of per array item code

GUILayout.BeginHorizontal();
GUILayout.Label("Type", GUILayout.Width(50));
type = EditorGUILayout.EnumPopup(); //this line is not code complete. fix to your requirements
GUILayout.Label("Strength", GUILayout.Width(50));
strength = GUILayout.TextField(strength, GUILayout.Width(100));
GUILayout.EndHorizontal();

I thought I read somewhere that EditorGUILayout didn’t work in property drawers?

Maybe this has been changed? I’ll have to test.

Yeah, you can’t use auto-layout in property drawers. Or… you can, but Unity spits errors at you, everything is slow, and nothing looks good.

@OP: might it be that LabelField respects indentLevel, but PropertyField doesn’t? It’s really poorly/not documented what indentLevel does in rect-based code. I know it does something, but I haven’t been able to nail down what.

Try to just do the indentation manually in the EmissionWrapper’s drawer, by moving the rect you draw the emissionProps to the right. I think that’s better code in general - the drawing code for the EmissionBlock shouldn’t be responsible for where it’s indented, whatever draws it should.

Actually, taking out indent does nothing. (changing the rects is not needed)
It was only there because, at the time, I was testing if putting it in would help.

@Desprez

Hi, so there’s nothing wrong / everything is working now?

Anyway, leaving indentLevel at 0 is probably good idea.

As AutoLayout is not supposed to be used with property drawers like @Baste mentioned, you’re left with Rects.

However, I don’t see a reason in your second script, why you would have those strange y-axis settings, or did that get fixed. Couldn’t know exactly what and how your setup is, but I tried to replicate it quickly. I set then item of array/list line like this:

Rect textR = new Rect(position.x, position.y, 50f, lh1);
Rect typeR = new Rect(position.x + 52, position.y, 60f, lh1);
Rect text2R = new Rect(position.x + 120, position.y, 60f, lh1);
Rect strengthR = new Rect(position.x + 180f, position.y, 60f, lh1);

As you don’t edit position, you just add to same unchanged position value some offsets.

And then I made a Emissions class, where I put an array of Emission (Emission[ ] emission). Using your “EmissionWrapper” PropertyDrawer class on this class created properly working +/- buttons and properly formatted list under it. So I don’t see any problems there.

However, to me it seems a bit strange how you have named your classes and Editor PropertyDrawer classes; Why not just make it like in manual (Class Ingredients and then Editor PropertyDrawer Class IngredientsDrawer) ?

[CustomPropertyDrawer (typeof (FS_Detector.EmissionBlock))]
public class EmissionBlockDrawer : PropertyDrawer

Well, no. In order to produce the orderly layout in the picture. I need the weird numbers in the script.
Setting the indent level to something other than 0 will change the indent accordingly, but otherwise the positions will remain unchanged relative to each other.

If I use the same numbers that you used, it looks like this:

@Desprez

OK… I only did the changes I mentioned, and everything seemed to be lined up properly like I would have expected, using offsets I pasted above. Maybe there is something else going on… My result looks like the image you have in your original post.

Yeah, clearly something else is going on. I’m guessing some other drawer affecting the script must be interfering somehow… not sure how it would though. I’ll have to start stripping away parts to isolate the conflict.

It turns out that a custom property attribute was interfering with the placement.

1 Like