Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question UIElements, PropertyField: indentLevel workaround

Discussion in 'UI Toolkit' started by MiniBeatBoy, Jul 26, 2021.

  1. MiniBeatBoy

    MiniBeatBoy

    Joined:
    Sep 23, 2013
    Posts:
    15
    Hi, I'm creating a custom editor with the UIElements and I want some PropertyFields to be indented. I found a temporary workaround and it is to set the leftPadding of the PropertyField to 15:

    Code (CSharp):
    1. m_SelectedSkinSelfLitField.style.paddingLeft = 15;
    2. m_ScaleInContainerField.style.paddingLeft = 15;
    but it also moves the input fields to the right:
    upload_2021-7-26_16-50-40.png

    I tried to get the label that is inside the PropertyField to assign the leftPadding to it, but it seems to hacky for me. By the way, I'm using Unity 2019.4.8

    Has anyone needed to solve this? Is there a simple way to do it?

    Thanks!
     
  2. sebastiend-unity

    sebastiend-unity

    Unity Technologies

    Joined:
    Nov 9, 2015
    Posts:
    183
    Some alignment fixes are coming in the later versions of the editor and in the package but we do not cover 2019.4 unfortunately.
     
  3. sebastiend-unity

    sebastiend-unity

    Unity Technologies

    Joined:
    Nov 9, 2015
    Posts:
    183
    I'm afraid for your case specifically, there is no better way than to edit the padding yourself :(
     
  4. MiniBeatBoy

    MiniBeatBoy

    Joined:
    Sep 23, 2013
    Posts:
    15
    Thanks @sebastiend-unity. I'm good with editing the padding myself ;) However I would like to know if there is a reliable way of obtaining a reference to the child Label element and apply the padding to it? Because otherwise, when I set the padding of the PropertyField, the child input field is also moved, which looks messy. I'm relatively new to UIElements so maybe I'm missing an easy way to query it. I would appreciate any guidance on this!
     
  5. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    179
    Isn't
    Q<Label>()
    reliable for you?
    upload_2021-7-31_17-47-46.png
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEditor.UIElements;
    3. using UnityEngine;
    4. using UnityEngine.UIElements;
    5.  
    6. public class TestWindow : EditorWindow {
    7.     const int LEVEL2_PADDING = 15;
    8.  
    9.     [MenuItem("Window/Test")]
    10.     public static void ShowWindow() {
    11.         GetWindow<TestWindow>("Test").Show();
    12.     }
    13.  
    14.     void CreateGUI() {
    15.         rootVisualElement.Add(new Toggle("Override Selected Skin Self Lit") { value = true });
    16.         var selectedSkinSelfLit = new Toggle("Selected Skin Self Lit");
    17.         selectedSkinSelfLit.Q<Label>().style.paddingLeft = LEVEL2_PADDING;
    18.         rootVisualElement.Add(selectedSkinSelfLit);
    19.  
    20.         rootVisualElement.Add(new Vector3Field("Position In Container"));
    21.  
    22.         rootVisualElement.Add(new Toggle("Modify Scale") { value = true });
    23.         var scaleInContainer = new Vector3Field("Scale In Container") { value = Vector3.one };
    24.         scaleInContainer.Q<Label>().style.paddingLeft = LEVEL2_PADDING;
    25.         rootVisualElement.Add(scaleInContainer);
    26.     }
    27. }
     
  6. MiniBeatBoy

    MiniBeatBoy

    Joined:
    Sep 23, 2013
    Posts:
    15
    Thanks @RunninglVlan. It seems that the way you are doing it is reliable, but my specific case is with
    PropertyField
    :

    Code (CSharp):
    1. var selectedSkinSelfLit = new PropertyField(serializedObject.FindProperty("SelectedSkinSelfLit"));
    2. selectedSkinSelfLit.Q<Label>().style.paddingLeft = 15;
    3. m_Inspector.Add(selectedSkinSelfLit);
    In this case, it is throwing a null exception because it seems that the
    PropertyField
    creates the child components later. (At least this is happening in 2019.4.8). I tried waiting for few frames and in fact after a few ones, the
    Label
    is found but the
    Label
    is not refreshed so the padding is not applied. So I tried
    MarkDirtyRepaint()
    and it does the job, but for me this looks a bit too hacky. I'm thinking there should be a better way to do it. Otherwise I should fallback on the non-property fields and do the binding with the property manually, but that's sad...
     
    Last edited: Aug 1, 2021
  7. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    179
    I guess you have some
    CustomEditor
    for your
    MonoBehaviour
    ? Could you provide relevant code of these 2 classes? I could reproduce your issue and check how it can be done non-hacky way ;)
     
    Last edited: Aug 2, 2021
  8. MiniBeatBoy

    MiniBeatBoy

    Joined:
    Sep 23, 2013
    Posts:
    15
    Yes, it is an inspector and the relevant editor code is basically what I posted before:
    Code (CSharp):
    1. PropertyField selectedSkinSelfLitField = new PropertyField(serializedObject.FindProperty("SelectedSkinSelfLit"));
    2. selectedSkinSelfLitField.Q<Label>().style.paddingLeft = 15; // <- Here the Label is null. It won't be null few frames later.
    3. m_Inspector.Add(selectedSkinSelfLitField);
    On the
    MonoBehaviour 
    side I have
    public bool SelectedSkinSelfLit;
    and I want it to be indented because it appears only if another bool is checked (
    OverrideSelectedSkinSelfLit
    ).

    By using the
    PropertyField
    , the serialized property is automatically bound to the controls so I wouln't need to do anything else.

    For me is it a bit strange that the label is not available after creating the
    PropertyField 
    instance, specially because in the other components, like the
    Toggle
    , the label is ready instantly. I guess it may be some type of deferred initialization.
     
    Last edited: Aug 2, 2021
  9. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    179
    I guess
    PropertyField
    doesn't add
    Label
    right away because it doesn't have some data at that moment. If I understood the source code correctly, PropertyField internals are added after property binding.
    Anyway you could overcome this by using
    RegisterCallback<GeometryChangedEvent>

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Test : MonoBehaviour {
    4.     public bool overrideSelectedSkinSelfLit = true;
    5.     public bool selectedSkinSelfLit;
    6.     public Vector3 positionInContainer;
    7.     public bool modifyScale = true;
    8.     public Vector3 scaleInContainer = Vector3.one;
    9. }
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEditor.UIElements;
    3. using UnityEngine.UIElements;
    4.  
    5. [CustomEditor(typeof(Test))]
    6. public class TestEditor : UnityEditor.Editor {
    7.     public override VisualElement CreateInspectorGUI() {
    8.         var container = new VisualElement();
    9.  
    10.         container.Add(new PropertyField(serializedObject.FindProperty(nameof(Test.overrideSelectedSkinSelfLit))));
    11.         var selectedSkinSelfLit = new PropertyField(serializedObject.FindProperty(nameof(Test.selectedSkinSelfLit)));
    12.         selectedSkinSelfLit.RegisterCallback<GeometryChangedEvent>(AddPadding);
    13.         container.Add(selectedSkinSelfLit);
    14.  
    15.         container.Add(new PropertyField(serializedObject.FindProperty(nameof(Test.positionInContainer))));
    16.  
    17.         var modifyScale = new PropertyField(serializedObject.FindProperty(nameof(Test.modifyScale)));
    18.         modifyScale.RegisterCallback<GeometryChangedEvent>(AddPadding);
    19.         container.Add(modifyScale);
    20.         container.Add(new PropertyField(serializedObject.FindProperty(nameof(Test.scaleInContainer))));
    21.  
    22.         return container;
    23.     }
    24.  
    25.     private static void AddPadding(GeometryChangedEvent evt) {
    26.         var element = evt.target as VisualElement;
    27.         element.Q<Label>().style.paddingLeft = 15;
    28.         element!.UnregisterCallback<GeometryChangedEvent>(AddPadding);
    29.     }
    30. }
     
  10. MiniBeatBoy

    MiniBeatBoy

    Joined:
    Sep 23, 2013
    Posts:
    15
    Awesome! This is pretty neat :) Thanks