Monday, May 20, 2024
HomeGame Developmentunity - How can I use `UnityEngine.UIElements.PopupWindow` for a custom PropertyDrawer targeting...

unity – How can I use `UnityEngine.UIElements.PopupWindow` for a custom PropertyDrawer targeting an array with UI Toolkit?


I’m following the “Create a Custom Inspector” car and tire example that Unity has in their documentation for UI Toolkit, but I’m trying to modify it in a specific way and I’m coming up short (whether or not it is a “good” way is beyond the scope of the question). Essentially, I’d like to take the editor display for an array and make it accessible by a single button that opens a PopupWindow in the editor, displaying the information for all four tires. I’d also like to make sure that the array is not reorderable, and if possible, I’d like to have it not act like a foldout in the PopupWindow.

I’ve managed to get this so far:

a sort-of functional popup behind a Button that does not display the information from TireSpecs.cs

In Car.cs, I have the following:

using UnityEngine;

public class Car : MonoBehaviour
{
    public string myMake = "Ford";
    public int myYearBuilt = 2001;
    public Color myColor = new Color(0.5725f, 0.525f, 0.3875f, 1.0f);
    
    public TireSpecs[] myTires = new TireSpecs[4];
    public ActualTireDetails tireOptions;
}

In TireSpecs.cs, I have the following:

using System;
using UnityEngine;

[Serializable]
public class TireSpecs
{
    public float myAirPressure = 35.0f;
    public int myProfileDepth = 4;

    public TireSpecs(float airPressure, int profileDepth)
    {
        myAirPressure = airPressure;
        myProfileDepth = profileDepth;
    }
}

And my PropertyDrawer, TirePropertyDrawer.cs, is structured like this (has been edited to reflect changes below):

using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor;
using UnityEditor.UIElements;
using System.Collections.Generic;

// Instead, writing [CustomPropertyDrawer(typeof(TireSpecs[]))] 
// will display the information of the tires in a reorderable array,
// but without the button or popup window

[CustomPropertyDrawer(typeof(TireSpecs))]
public class TirePropertyDrawer : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        var container = new VisualElement();

        var button = new Button(() => ShowPopup(container, property)) { text = "Open Popup" };
        container.Add(button);

        return container;
    }

    void ShowPopup(VisualElement anchor, SerializedProperty property)
    {
        UnityEngine.UIElements.PopupWindow popup = new UnityEngine.UIElements.PopupWindow();
        popup.Add(new Label("This is a Popup"));
        popup.style.flexGrow = 200;
        popup.style.flexDirection = FlexDirection.Row;

        SerializedProperty parentProperty = property.GetParentProperty();


        if (parentProperty != null && parentProperty.isArray)
        {
            Debug.Log($"Array Size: {parentProperty.arraySize}");

            for (int i = 0; i < parentProperty.arraySize; i++)
            {
                SerializedProperty arrayElement = parentProperty.GetArrayElementAtIndex(i);
                VisualElement arrayElementContainer = new VisualElement();

                arrayElementContainer.Add(new Label($"Tire #{i + 1}: "));
                arrayElementContainer.Add(new PropertyField(arrayElement.FindPropertyRelative("myAirPressure"), "Pressure: "));
                arrayElementContainer.Add(new PropertyField(arrayElement.FindPropertyRelative("myProfileDepth"), "Profile Depth: "));
                arrayElementContainer.style.paddingTop = 16;
                arrayElementContainer.style.paddingRight = 16;
                arrayElementContainer.style.paddingBottom = 16;
                arrayElementContainer.style.paddingLeft = 16;

                popup.Add(arrayElementContainer);
            }  
        }

        var button = new Button(() => anchor.Remove(popup)) { text = "Close Popup" };
        popup.Add(button);
        anchor.Add(popup);
    }
}

// Extension method to get the parent property, which may be an array
public static class SerializedPropertyExtensions
{
    public static SerializedProperty GetParentProperty(this SerializedProperty property)
    {
        string propertyPath = property.propertyPath;
        int lastDotIndex = propertyPath.LastIndexOf('.');
        if (lastDotIndex >= 0)
        {
            string parentPath = propertyPath.Substring(0, lastDotIndex);
            return property.serializedObject.FindProperty(parentPath);
        }
        return null;
    }
}

EDIT: After consulting with some folks, I’ve managed to get it to this stage:

an update of the .gif above, where the popup window now properly displays some text, but not the fields from TireSpecs.cs

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments