Как правильно отрисовывать CustomPropertyDrawer

Рейтинг: 0Ответов: 2Опубликовано: 14.02.2023

Встала необходимость отрисовывать в редакторе сериализуемые классы определённым образом.

Есть кастомный редактор для объекта, который имеет вид: введите сюда описание изображения

Рисуются построчно одиночные поля и массивы, за ними есть 3 кнопки. Всё хорошо и замечательно.

Пробую сделать то же самое, но отрисовку полей и массивов делаю с помощью CustomPropertyDrawer. Сразу же натыкаюсь на такое:

введите сюда описание изображения

То есть отрисовывается проперти, внутри которого отступы нормальные, а когда очередь доходит до отрисовки кнопок, они отрисовываются поверх, как будто весь проперти занимал 1 строку,а не несколько. Как с этим бороться? Блок кода редактора, где рисуется проперти и кнопки:

var prop = serializedObject.FindProperty(nameof(crt.displayedDimension));
    PropertyField(prop);
    serializedObject.ApplyModifiedProperties();


 BeginHorizontal("box");
    if (GUILayout.Button("Copy dimension"))
    {
        copyMatrix = new MatrixDimension<T>(cr.Dimensions[cr.currentDimensionIndex]);
    }
    if (GUILayout.Button("Paste copied dimension"))
    {
        confirmPressed = !confirmPressed;
        if (copyMatrix != null)
            onPress = () => { cr.Dimensions[cr.currentDimensionIndex] = new MatrixDimension<T>(copyMatrix); };

    }
    if (GUILayout.Button("Clear dimension"))
    {
        confirmPressed = !confirmPressed;
        onPress = () => { cr.Dimensions[cr.currentDimensionIndex] = new MatrixDimension<T>(cr.ColumnsCount); };
    }
    EndHorizontal();

Код отрисовки проперти:

 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    float space = 2f;
    float labelSize = 150f;
    var scaleFloat = property.FindPropertyRelative("scallingValue");
    var dimensionName = property.FindPropertyRelative("dimensionName");
    var namesArr = property.FindPropertyRelative("columnsNames.vector");
    BeginProperty(position, label, property);

    scaleFloat.floatValue = FloatField(position, new GUIContent("Dimension scale"), scaleFloat.floatValue);
    position.y += position.height + space;
    dimensionName.stringValue = TextField(position, new GUIContent("Dimension name"), dimensionName.stringValue);
    position.y += position.height + space;
    position = DrawArrayOneLine(position, labelSize,string.Empty, namesArr);
    

    EndProperty();
    property.serializedObject.ApplyModifiedProperties();
}

Ответы

▲ 1

Да, GUILayout отрисовывает все в Editor по строчно сам следя за шириной и позицией, но в PropertyDrawer такой роскоши нет, ширина/высота/позиция прописывается вручную.


Высота поля определяется через метод GetPropertyHeight.

public override float GetPropertyHeight (SerializedProperty property, GUIContent label)
{
    int lineCount = 3;
    if (property.isExpanded)
        return EditorGUIUtility.singleLineHeight*lineCount+
            EditorGUIUtility.standardVerticalSpacing*(lineCount-1);
    else
        return EditorGUIUtility.singleLineHeight;
}

Причем в зависимости от того свернуто поле объекта или нет, она разная. Статус свернутости определяется значением SerializedProperty.isExpanded.


Для правильной размерности полезны стандартные значения:

  • EditorGUIUtility.singleLineHeight стандартная высота строки редактора
  • EditorGUIUtility.standardVerticalSpacing стандартный пробел между строками
  • EditorGUIUtility.currentViewWidth текущая ширина окна (инспектора в данном контексте). Но в PropertyDrawer коректнее использовать SerializedProperty.width.

EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

var labelRect1 = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
position.y += EditorGUIUtility.singleLineHeigh+EditorGUIUtility.standardVerticalSpacing;
var labelRect2 = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
position.y += EditorGUIUtility.singleLineHeigh+EditorGUIUtility.standardVerticalSpacing;
var labelRect3 = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);

И все будет выглядеть словно отрисовывал GUILayout.


Не забываем про документацию . По поиску height, можно было найти ответ.

▲ 0

Решение найдено. Необходимо было переопределить GetPropertyHeigh в Drawer'е чтобы итоговый размер Rect для проперти был достаточный для отрисовки полей.

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    var totalLines = 51;
    return totalLines * EditorGUIUtility.singleLineHeight + verticalSpace * (totalLines -1);
}

Хороший тутор по теме: https://nosuchstudio.medium.com/learn-unity-editor-scripting-property-drawers-part-2-6fe6097f1586