dbrizov / NaughtyAttributes

Attribute Extensions for Unity
MIT License
4.51k stars 465 forks source link

[Issue] ShowIf / EnableIf does not work with Arrays or Lists in nested classes #142

Open jurcaua opened 4 years ago

jurcaua commented 4 years ago
[ShowIf("ShowIfTest")]
public int[] test;
public bool ShowIfTest => false;

Here is the code I used to test it, no nesting. It should be hidden, but no matter what, it will always show. Tried with List as well.

I tried to search around current and past issues, but nothing came up on this issue. Is there any workaround for this issue, or did I miss the proper way to do this?

Thank you for all your work!

dbrizov commented 4 years ago

It works on my end mate. Can you try it on an empty project? It should work. Probably some other editor script is overriding NaughtyAtribute's inspector.

jurcaua commented 4 years ago

Hello! Thank you for the quick response :)

I'm trying this now (which is closer to my own use-case), and I'm failing to see what I could be doing wrong here, or how something is overriding NaughtyAtribute's inspector... any tips on what to look for that could be overriding this?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyAttributes;

public enum TestEnum
{
    One,
    Two
}

[System.Serializable]
public class TestClass
{
    public TestEnum enumValue;

    [ShowIf("ShowIfOne")]
    [AllowNesting]
    public int[] one;

    [ShowIf("ShowIfTwo")]
    [AllowNesting]
    public int[] two;

    public bool ShowIfOne => enumValue == TestEnum.One;
    public bool ShowIfTwo => enumValue == TestEnum.Two;
}

public class testsomething : MonoBehaviour
{
    public TestClass reactiveAction;
}

image image

dbrizov commented 4 years ago

This is indeed a bug. If not nested it works fine, but when nested inside a subclass it doesn't work. I will fix it mate, thank you very much.

The strangest thing is that it doesn't work for lists and array. Everything else works.

public class _NaughtyComponent : MonoBehaviour
{
    public NumEnum enumValue;

    [ShowIf("ShowIfOne")]
    public int[] one;

    [ShowIf("ShowIfTwo")]
    public int[] two;

    public bool ShowIfOne => enumValue == NumEnum.One;
    public bool ShowIfTwo => enumValue == NumEnum.Two;
}

image

jurcaua commented 4 years ago

Interesting! Thank you so much for looking into it :)

dbrizov commented 4 years ago

I understood why it's now working. Look at this example.

public class _NaughtyComponent : MonoBehaviour
{
    public MyClass nest;
}

[System.Serializable]
public class MyClass
{
    [ReadOnly]
    public int[] one;

    [ReadOnly]
    public int[] two;
}

image

Even though I've marked the arrays with [ReadOnly] the property drawer is applied to each element of the arrays, not the arrays themselves. This is how Unity applies CustomPropertyDrawers to lists/arrays. This is what happens with the [AllowNesting] attribute. [AllowNesting] is a normal drawer, it just triggers the meta attributes' behaviors (like [ShowIf] for instance). The problem is that NaughtyAttributes triggers the behaviors before the actual drawer is called. The drawer can't handle the visibility itself, because the parent property is already drawn. I can make it handle the visibility itself, but the result will be just like the [ReadOnly] attribute. The array will be visible, only the child elements will not.

The reason why the [ShowIf] works for not nested arrays is that I can get the array and say to Unity - "draw the property and its children". Before I draw the property and its children, I check if it is visible. If it's not I don't draw it at all.

I can't think of a way to fix it :(

jurcaua commented 4 years ago

I'm not very sure either how to fix it :(

I guess for now, if you ever find some sort of workaround, let me know!! Would be very useful to have! Thank you so much for investigating the issue however, I really appreciate it :)

RunninglVlan commented 4 years ago

Hi, I don't know exactly how it's implemented in Naughty, but until this is fixed, here's a temporary fix we used on our end - a drawer for @jurcaua's TestClass:

using NaughtyAttributes.Editor;
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(TestClass))]
public class TestClassDrawer : PropertyDrawer {
    public override void OnGUI(Rect _, SerializedProperty property, GUIContent __) {
        var childDepth = property.depth + 1;
        EditorGUILayout.PropertyField(property, includeChildren: false);
        if (!property.isExpanded) {
            return;
        }
        EditorGUI.indentLevel++;
        foreach (SerializedProperty child in property) {
            if (child.depth == childDepth && PropertyUtility.IsVisible(child)) {
                EditorGUILayout.PropertyField(child);
            }
        }
        EditorGUI.indentLevel--;
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
        return EditorGUI.GetPropertyHeight(property, label, includeChildren: false)
                - EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing;
    }
}

This is the code I used for our task, so it might have some extra lines, but it worked without any modifications for TestClass too ;)

dbrizov commented 4 years ago

Hi, I don't know exactly how it's implemented in Naughty, but until this is fixed, here's a temporary fix we used on our end - a drawer for @jurcaua's TestClass:

using NaughtyAttributes.Editor;
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(TestClass))]
public class TestClassDrawer : PropertyDrawer {
    public override void OnGUI(Rect _, SerializedProperty property, GUIContent __) {
        var childDepth = property.depth + 1;
        EditorGUILayout.PropertyField(property, includeChildren: false);
        if (!property.isExpanded) {
            return;
        }
        EditorGUI.indentLevel++;
        foreach (SerializedProperty child in property) {
            if (child.depth == childDepth && PropertyUtility.IsVisible(child)) {
                EditorGUILayout.PropertyField(child);
            }
        }
        EditorGUI.indentLevel--;
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
        return EditorGUI.GetPropertyHeight(property, label, includeChildren: false) - 20;
    }
}

This is the code I used for our task, so it might have some extra lines, but it worked without any modifications for TestClass too ;)

Thanks, I'll look into it

TheCaveOfWonders commented 1 year ago

@dbrizov was this fixed? I'm still experiencing the same issue in v2.1.4

ninjuit commented 11 months ago

My solution for this was to create a NestedArray<T> class with an Array/List inside. It's a bit unwieldy (adds an extra layer of depth to the inspector hierarchy) but at least I can Show/Hide collections.

[System.Serializable]
public class NestedArray<T>
{
    public T[] Array;
}
ZeroUltra commented 7 months ago

My solution for this was to create a NestedArray<T> class with an Array/List inside. It's a bit unwieldy (adds an extra layer of depth to the inspector hierarchy) but at least I can Show/Hide collections.

[System.Serializable]
public class NestedArray<T>
{
    public T[] Array;
}

ye! i make HideNestedArrayAttribute

using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// ____DESC:      
/// </summary>
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public class HideNestedArrayAttribute : PropertyAttribute
{

}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(HideNestedArrayAttribute))]
public class HideNestedArrayDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var array = property.FindPropertyRelative("Array");
        EditorGUI.PropertyField(position, array, new GUIContent(property.displayName));
    }
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        var array = property.FindPropertyRelative("Array");
  // These numbers are tried out. I don't know how to get it.
        if (array.isExpanded)
        {
            return 70 + Mathf.Clamp((array.arraySize-1) * 20, 0, float.MaxValue);
        }
        else
            return 20;
    }
}
#endif

public class NewMonoBehaviour : MonoBehaviour
{
    public bool useIds;
    [HideNestedArray, ShowIf("useIds")]
    public NestedArray<int> ids;

    public TestArray testArray ;

    [System.Serializable]
    public class TestArray
    {
        public bool useIds;
        [ShowIf("useIds"), AllowNesting, HideNestedArray]
        public NestedArray<int> ids;
    }
}

image