Search Unity

Draw a field only if a condition is met

Discussion in 'Scripting' started by Or-Aviram, Jan 1, 2017.

  1. Or-Aviram

    Or-Aviram

    Joined:
    Dec 31, 2015
    Posts:
    6
    Until now when I wanted to make a field appear in the inspector I made a custom inspector, but I got tired of it, so I decided to make an attribute.

    The files needed:
    1.
    First of all, I used something that I posted about in another please. You can find it here, get it before you get the attribute.
    2. Put this enum wherever you want:
    Code (CSharp):
    1. /// <summary>
    2. /// Types of comperisons.
    3. /// </summary>
    4. public enum ComparisonType
    5. {
    6.     Equals = 1,
    7.     NotEqual = 2,
    8.     GreaterThan = 3,
    9.     SmallerThan = 4,
    10.     SmallerOrEqual = 5,
    11.     GreaterOrEqual = 6
    12. }
    3. Put this enum wherever you want as well:
    Code (CSharp):
    1. /// <summary>
    2. /// Types of comperisons.
    3. /// </summary>
    4. public enum DisablingType
    5. {
    6.     ReadOnly = 2,
    7.     DontDraw = 3
    8. }

    4. Put this inside of a CSharp file wherever you want in your project:
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3.  
    4. /// <summary>
    5. /// Draws the field/property ONLY if the copared property compared by the comparison type with the value of comparedValue returns true.
    6. /// </summary>
    7. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
    8. public class DrawIfAttribute : PropertyAttribute
    9. {
    10.     public string comparedPropertyName { get; private set; }
    11.     public object comparedValue { get; private set; }
    12.     public ComparisonType comparisonType { get; private set; }
    13.     public DisablingType disablingType { get; private set; }
    14.  
    15.     /// <summary>
    16.     /// Only draws the field only if a condition is met.
    17.     /// </summary>
    18.     /// <param name="comparedPropertyName">The name of the property that is being compared (case sensitive).</param>
    19.     /// <param name="comparedValue">The value the property is being compared to.</param>
    20.     /// <param name="comparisonType">The type of comperison the values will be compared by.</param>
    21.     /// <param name="disablingType">The type of disabling that should happen if the condition is NOT met. Defaulted to DisablingType.DontDraw.</param>
    22.     public DrawIfAttribute(string comparedPropertyName, object comparedValue, ComparisonType comparisonType, DisablingType disablingType = DisablingType.DontDraw)
    23.     {
    24.         this.comparedPropertyName = comparedPropertyName;
    25.         this.comparedValue = comparedValue;
    26.         this.comparisonType = comparisonType;
    27.         this.disablingType = disablingType;
    28.     }
    29. }
    5. Put this wherever you want, just make sure it's either directly or indirectly in a folder called "Editor":
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomPropertyDrawer(typeof(DrawIfAttribute))]
    5. public class DrawIfPropertyDrawer : PropertyDrawer
    6. {
    7.     // Reference to the attribute on the property.
    8.     DrawIfAttribute drawIf;
    9.  
    10.     // Field that is being compared.
    11.     SerializedProperty comparedField;
    12.  
    13.     // Height of the property.
    14.     private float propertyHeight;
    15.  
    16.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    17.     {
    18.         return propertyHeight;
    19.     }
    20.  
    21.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    22.     {
    23.         // Set the global variables.
    24.         drawIf = attribute as DrawIfAttribute;
    25.         comparedField = property.serializedObject.FindProperty(drawIf.comparedPropertyName);
    26.  
    27.         // Get the value of the compared field.
    28.         object comparedFieldValue = comparedField.GetValue<object>();
    29.  
    30.         // References to the values as numeric types.
    31.         NumericType numericComparedFieldValue = null;
    32.         NumericType numericComparedValue = null;
    33.  
    34.         try
    35.         {
    36.             // Try to set the numeric types.
    37.             numericComparedFieldValue = new NumericType(comparedFieldValue);
    38.             numericComparedValue = new NumericType(drawIf.comparedValue);
    39.         }
    40.         catch (NumericTypeExpectedException)
    41.         {
    42.             // This place will only be reached if the type is not a numeric one. If the comparison type is not valid for the compared field type, log an error.
    43.             if (drawIf.comparisonType != ComparisonType.Equals && drawIf.comparisonType != ComparisonType.NotEqual)
    44.             {
    45.                 Debug.LogError("The only comparsion types available to type '" + comparedFieldValue.GetType() + "' are Equals and NotEqual. (On object '" + property.serializedObject.targetObject.name + "')");
    46.                 return;
    47.             }
    48.         }
    49.  
    50.         // Is the condition met? Should the field be drawn?
    51.         bool conditionMet = false;
    52.  
    53.         // Compare the values to see if the condition is met.
    54.         switch (drawIf.comparisonType)
    55.         {
    56.             case ComparisonType.Equals:
    57.                 if (comparedFieldValue.Equals(drawIf.comparedValue))
    58.                     conditionMet = true;
    59.                 break;
    60.  
    61.             case ComparisonType.NotEqual:
    62.                 if (!comparedFieldValue.Equals(drawIf.comparedValue))
    63.                     conditionMet = true;
    64.                 break;
    65.  
    66.             case ComparisonType.GreaterThan:
    67.                 if (numericComparedFieldValue > numericComparedValue)
    68.                     conditionMet = true;
    69.                 break;
    70.  
    71.             case ComparisonType.SmallerThan:
    72.                 if (numericComparedFieldValue < numericComparedValue)
    73.                     conditionMet = true;
    74.                 break;
    75.  
    76.             case ComparisonType.SmallerOrEqual:
    77.                 if (numericComparedFieldValue <= numericComparedValue)
    78.                     conditionMet = true;
    79.                 break;
    80.  
    81.             case ComparisonType.GreaterOrEqual:
    82.                 if (numericComparedFieldValue >= numericComparedValue)
    83.                     conditionMet = true;
    84.                 break;
    85.         }
    86.  
    87.         // The height of the property should be defaulted to the default height.
    88.         propertyHeight = base.GetPropertyHeight(property, label);
    89.    
    90.         // If the condition is met, simply draw the field. Else...
    91.         if (conditionMet)
    92.         {
    93.             EditorGUI.PropertyField(position, property);
    94.         }
    95.         else
    96.         {
    97.             //...check if the disabling type is read only. If it is, draw it disabled, else, set the height to zero.
    98.             if (drawIf.disablingType == DisablingType.ReadOnly)
    99.             {
    100.                 GUI.enabled = false;
    101.                 EditorGUI.PropertyField(position, property);
    102.                 GUI.enabled = true;
    103.             }
    104.             else
    105.             {
    106.                 propertyHeight = 0f;
    107.             }
    108.         }
    109.     }
    110. }
    This is how you can use it:
    This is just an example script I put on one of the game objects:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Test : MonoBehaviour
    4. {
    5.     [Range(0, 2)]
    6.     public float someFloat;
    7.  
    8.     [DrawIf("someFloat", 1f, ComparisonType.GreaterOrEqual)]
    9.     public int someInt;
    10.  
    11.     public bool someBool;
    12.  
    13.     [DrawIf("someBool", true, ComparisonType.Equals, DisablingType.ReadOnly)]
    14.     public int anotherInt;
    15. }
    This is the result:


    Just keep in mind a few things:
    • When the condition isn't met and it doesn't draw the field, when it is met and it draws it it will take a very little bit of time to update the height (you can see it in the GIF).
    • When you spell the name of the compared field incorrectly, it won't give you an error. So make sure the spelling is correct and the capital letters are correct as well (it's case sensitive).

    EDIT:

    I originally wrote "comperison", instead of "comparison". I tried to fix it in all of the places, but if anyone found any more mistakes (since I think I missed some), please tell me.

    For anyone who doesn't want to copy the code, you download it here. Just get the "Hide If Attribute" folder. You can also try the numeric type one: https://drive.google.com/drive/folders/0BwN2sNbqJn9mLXNqNnFWd2dUUFk?usp=sharing
     
    Last edited: Jan 1, 2017
  2. gorbit99

    gorbit99

    Joined:
    Jul 14, 2015
    Posts:
    1,350
    First of all, Comparison
    Then wouldn't it be better if you posted it as a single cs file and told us, that it needs to be in thEditor folder?
     
  3. Or-Aviram

    Or-Aviram

    Joined:
    Dec 31, 2015
    Posts:
    6
    Ugh, I knew it was comparison... For some reason I just went with e. I don't know why, I was probably just dumb. I will fix it.
    And what doesn the second line mean? I don't get it.
     
    SamFernGamer4k likes this.
  4. gorbit99

    gorbit99

    Joined:
    Jul 14, 2015
    Posts:
    1,350
    It means, that instead of telling us to copy a bunch of code somewhere in our project, you should put the whole thing into a c# file and post that instead
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Really nice!

    I've got the same thing going in our project (think I've posted it earlier), though I use a "HideIf" attribute. Since showing is default, I guess I fealt that the attribute that you put on should have the name of what you're changing it to? idk.

    I've also split the different kinds of attributes into different attribute types. So a hider that checks a bool (HideIf) has a different type from a hider that checks for objects (ie. HideIfNull/HideIfNotNull) and the hider for enum values (HideIfEnumValue). I've never had the need for number-based comparrison.

    I did that because they've got different needs. The bool one needs to check either against true of false, while the Enum one checks against an array of possible values.


    I really like the disableType, I'll probably grab that. I've just been completely hiding things, didn't think of the disabling possibility.

    The height changing is a shame, but nothing we can do anything about. As far as I can tell, the editor is drawn in two passes - the first pass calculates the position of everything, while the second does the drawing and the input. The problem is that your input can change the required height, but that's not handled in the layout phase.

    @gorbit99, don't be lazy! Copy-pasting ain't that hard. Having the code in the post is also a lot nicer for people who are super-paranoid about downloading stuff from posts.
     
  6. gorbit99

    gorbit99

    Joined:
    Jul 14, 2015
    Posts:
    1,350
    @Baste I'm not lazy, I'm not even planning on using this
     
  7. Or-Aviram

    Or-Aviram

    Joined:
    Dec 31, 2015
    Posts:
    6
    Hmm...sure. I actually thought about doing it but I was too lazy to do it, since I thought most people don't talk.
    Anyways, I edited the post. You can download it now.
     
  8. Or-Aviram

    Or-Aviram

    Joined:
    Dec 31, 2015
    Posts:
    6
    Thanks.

    I don't really know... "DrawIf" just popped in my mind. I don't think I will change that.

    I didn't split the different kinds of attributes because it feels kinda inefficient. It did actually get me into a lot of trouble to only use one attribute, but it ended up giving me that NumericType thing, which I really like. And it's more efficient.
    I actually only had the need to use a boolean in my project, the others are just in case later I will use them.
     
  9. gorbit99

    gorbit99

    Joined:
    Jul 14, 2015
    Posts:
    1,350
    Line 2 of the first and second code and line 20 in the third still contains a comperison
     
  10. Or-Aviram

    Or-Aviram

    Joined:
    Dec 31, 2015
    Posts:
    6
    Updated, hopefully I finally fixed all of the mistakes.
     
  11. Karsnen_2

    Karsnen_2

    Joined:
    Nov 28, 2011
    Posts:
    89
    @Ov-Aviram - Great job.
     
  12. DreamingSpirit

    DreamingSpirit

    Joined:
    Apr 29, 2013
    Posts:
    8
    I know it is a while ago, but I was actually interested in using this script. It didn't work for me since I was missing the GetValue extension method for serializedproperties. I made some modifications to the script so that it just checks and shows the properties when equal to bool and enums, which is what I needed. I also circumvented the height issue.

    Example
    For those of you who are interested in a property drawer that can hide and show elements like so:
    Code (CSharp):
    1.    public enum ShowValueEnum
    2.     {
    3.         ShowValue1,
    4.         ShowValue2,
    5.         None
    6.     }
    7.  
    8.     [Header("Show Value?")]
    9.  
    10.     // An example with an enum
    11.     public ShowValueEnum EnumTest = ShowValueEnum.None;
    12.     [DrawIf("EnumTest", ShowValueEnum.ShowValue1)]  //Show if enum is equal to ShowValue1
    13.     public int Value1 = 100;
    14.     [DrawIf("EnumTest", ShowValueEnum.ShowValue2)]  //Show if enum is equal to ShowValue2
    15.     public int Value2 = 200;
    16.  
    17.     // An example for a bool
    18.     public bool BoolTest = false;
    19.     [DrawIf("BoolTest", true)] // Show if booltest bool is true
    20.     public Vector3 Value;


    Attribute script:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4.  
    5. /// <summary>
    6. /// Draws the field/property ONLY if the compared property compared by the comparison type with the value of comparedValue returns true.
    7. /// Based on: https://forum.unity.com/threads/draw-a-field-only-if-a-condition-is-met.448855/
    8. /// </summary>
    9. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
    10. public class DrawIfAttribute : PropertyAttribute
    11. {
    12.     #region Fields
    13.  
    14.     public string comparedPropertyName { get; private set; }
    15.     public object comparedValue { get; private set; }
    16.     public DisablingType disablingType { get; private set; }
    17.  
    18.     /// <summary>
    19.     /// Types of comperisons.
    20.     /// </summary>
    21.     public enum DisablingType
    22.     {
    23.         ReadOnly = 2,
    24.         DontDraw = 3
    25.     }
    26.  
    27.     #endregion
    28.  
    29.     /// <summary>
    30.     /// Only draws the field only if a condition is met. Supports enum and bools.
    31.     /// </summary>
    32.     /// <param name="comparedPropertyName">The name of the property that is being compared (case sensitive).</param>
    33.     /// <param name="comparedValue">The value the property is being compared to.</param>
    34.     /// <param name="disablingType">The type of disabling that should happen if the condition is NOT met. Defaulted to DisablingType.DontDraw.</param>
    35.     public DrawIfAttribute(string comparedPropertyName, object comparedValue, DisablingType disablingType = DisablingType.DontDraw)
    36.     {
    37.         this.comparedPropertyName = comparedPropertyName;
    38.         this.comparedValue = comparedValue;
    39.         this.disablingType = disablingType;
    40.     }
    41. }
    PropertyDrawer editor script (put inside editor folder):
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// Based on: https://forum.unity.com/threads/draw-a-field-only-if-a-condition-is-met.448855/
    7. /// </summary>
    8. [CustomPropertyDrawer(typeof(DrawIfAttribute))]
    9. public class DrawIfPropertyDrawer : PropertyDrawer
    10. {
    11.     #region Fields
    12.  
    13.     // Reference to the attribute on the property.
    14.     DrawIfAttribute drawIf;
    15.  
    16.     // Field that is being compared.
    17.     SerializedProperty comparedField;
    18.  
    19.     #endregion
    20.  
    21.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    22.     {
    23.         if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw)
    24.             return 0f;
    25.    
    26.         // The height of the property should be defaulted to the default height.
    27.         return base.GetPropertyHeight(property, label);
    28.     }
    29.  
    30.     /// <summary>
    31.     /// Errors default to showing the property.
    32.     /// </summary>
    33.     private bool ShowMe(SerializedProperty property)
    34.     {
    35.         drawIf = attribute as DrawIfAttribute;
    36.         // Replace propertyname to the value from the parameter
    37.         string path = property.propertyPath.Contains(".") ? System.IO.Path.ChangeExtension(property.propertyPath, drawIf.comparedPropertyName) : drawIf.comparedPropertyName;
    38.  
    39.         comparedField = property.serializedObject.FindProperty(path);
    40.  
    41.         if (comparedField == null)
    42.         {
    43.             Debug.LogError("Cannot find property with name: " + path);
    44.             return true;
    45.         }
    46.  
    47.         // get the value & compare based on types
    48.         switch (comparedField.type)
    49.         { // Possible extend cases to support your own type
    50.             case "bool":
    51.                 return comparedField.boolValue.Equals(drawIf.comparedValue);
    52.             case "Enum":
    53.                 return comparedField.enumValueIndex.Equals((int)drawIf.comparedValue);
    54.             default:
    55.                 Debug.LogError("Error: " + comparedField.type + " is not supported of " + path);
    56.                 return true;
    57.         }
    58.     }
    59.  
    60.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    61.     {
    62.         // If the condition is met, simply draw the field.
    63.         if (ShowMe(property))
    64.         {
    65.             EditorGUI.PropertyField(position, property);
    66.         } //...check if the disabling type is read only. If it is, draw it disabled
    67.         else if (drawIf.disablingType == DrawIfAttribute.DisablingType.ReadOnly)
    68.         {
    69.             GUI.enabled = false;
    70.             EditorGUI.PropertyField(position, property);
    71.             GUI.enabled = true;
    72.         }
    73.     }
    74.  
    75. }
    76.  
     
    Last edited: Mar 23, 2018
  13. Lofar42

    Lofar42

    Joined:
    Oct 11, 2014
    Posts:
    12
    angelomoro and Karsnen_2 like this.
  14. RealSoftGames

    RealSoftGames

    Joined:
    Jun 8, 2014
    Posts:
    220
    Hi i took bits and pieces from here as i only need a very basic setup for what im doing. but i have noticed i can not hide an array. the values will hide but the property label is still persistant?
     
  15. Arano

    Arano

    Joined:
    Aug 29, 2019
    Posts:
    7
    @Or-Aviram I am using your plugin at the moment, the conditional fields are awesome when working with non programmers on the same project.

    I did not find a git page for your project so i am sending a comment here, its just a small error that makes building impossible (this might not be the case in the original versions tho) anw..

    pull request: (sorta)
    can u put the file "SerializedPropertyExtentions.cs" inside the plugins/drawifattribute/editor folder?
    Unity gives me build errors when this is not the case.

    Thanks again for a great plugin.
     
  16. enesipek

    enesipek

    Joined:
    Jul 27, 2016
    Posts:
    1
    Thanks for everythink bro. U are sooo good mannn :D
     
  17. sayginkarahan

    sayginkarahan

    Joined:
    Jan 23, 2015
    Posts:
    49
    Thank you for sharing this amazing post @DreamingSpirit. But there is a little issue for arrays and list fields as @RealSoftGames mentioned. Do you know how to fix this issue, thanks?
     
    _TheFuture_ likes this.
  18. sayginkarahan

    sayginkarahan

    Joined:
    Jan 23, 2015
    Posts:
    49
    Also, thank you @Or-Aviram but your code doesn't work for arrays and lists too. Do you have any solution for this issue, thanks?
     
  19. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @sayginkarahan

    "Also, thank you @Or-Aviram but your code doesn't work for arrays and lists too. Do you have any solution for this issue, thanks?"

    ...you are asking a user last seen in 2017.

    Unity documentation has examples of how to create property drawer for a class, those property drawer drawn classes will look the same as a field or in a list or in an array:
    https://docs.unity3d.com/ScriptReference/PropertyDrawer.html
     
  20. gamedevgarage

    gamedevgarage

    Joined:
    Apr 21, 2018
    Posts:
    6
    Thanks! Saved my days! :D
     
  21. rahulunityproject

    rahulunityproject

    Joined:
    Apr 3, 2020
    Posts:
    1
    Thx Guys for this
     
  22. lunarcloud

    lunarcloud

    Joined:
    Mar 3, 2015
    Posts:
    2
    Last edited: Sep 16, 2020
  23. rioneye

    rioneye

    Joined:
    Aug 10, 2015
    Posts:
    12
    I had been fiddling with this for days before I realized I could just use ShowIf from the Odin inspector. Incredibly easy and painless.
     
  24. NamasPlumpus

    NamasPlumpus

    Joined:
    Oct 17, 2019
    Posts:
    2
    Sorry if this is a bit of a necro, but using the scripts from @DreamingSpirit works fine unless you want it to expand a class, struct, etc. I added a couple of lines to
    GetPropertyHeight
    so it expands without cutting off the fields in the class you're using the
    DrawIf
    attribute on. As a side note, I don't have much experience with property drawers and I'm not sure how good this is, but it works for my use.

    Code (CSharp):
    1. //In DrawIfPropertyDrawer.cs
    2. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    3. {
    4.     if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw)
    5.         return 0f;
    6.  
    7.     float totalHeight = EditorGUI.GetPropertyHeight(property, label) + EditorGUIUtility.standardVerticalSpacing;
    8.     while (property.NextVisible(true) && property.isExpanded)
    9.     {
    10.         totalHeight += EditorGUI.GetPropertyHeight(property, label, true) + EditorGUIUtility.standardVerticalSpacing;
    11.     }
    12.     return totalHeight;
    13. }
     
    _TheFuture_ and newlife like this.
  25. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,080
    Hello @NamasPlumpus, ufortunately in my case it doesnt seems to work.. It just add some random space but none of the class fields are showed (one enum field and and one int field). Any suggestion?
     
  26. newlife

    newlife

    Joined:
    Jan 20, 2010
    Posts:
    1,080
    This worked for me:
    Code (CSharp):
    1.      public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    2.     {
    3.           if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw) {
    4.               return -EditorGUIUtility.standardVerticalSpacing;
    5.         }
    6.         else
    7.         {
    8.             return EditorGUI.GetPropertyHeight(property, label);
    9.         }
    10.     }    
     
  27. NamasPlumpus

    NamasPlumpus

    Joined:
    Oct 17, 2019
    Posts:
    2
    I'm not sure why it wasn't showing your fields. It shows mine when I expand my classes. It's awesome you figured it out though (and in way more concise than my solution lol).
     
  28. Cloudwalker_

    Cloudwalker_

    Joined:
    Jan 3, 2014
    Posts:
    140
    Broken in 2020.3?

    Edit:

    EditorGUI.PropertyField no longer draws labels by default (even though the API says that they do ha)

    EditorGUI.PropertyField(position, property, label);
     
    Last edited: Mar 15, 2021
  29. Marks4

    Marks4

    Joined:
    Feb 25, 2018
    Posts:
    547
    In the DrawIfPropertyDrawer class, change
    Code (CSharp):
    1. case "Enum":
    2.                 return comparedField.enumValueIndex.Equals((int)drawIf.comparedValue);
    to
    Code (CSharp):
    1. case "Enum":
    2.                     return (comparedField.intValue & (int)drawIf.comparedValue) == (int)drawIf.comparedValue;
    to support enums being used as flags
     
  30. Marks4

    Marks4

    Joined:
    Feb 25, 2018
    Posts:
    547
    By the way showing conditional Unity Events is not working...
    upload_2021-3-18_2-10-33.png
     
  31. Andrewleek

    Andrewleek

    Joined:
    Jul 31, 2017
    Posts:
    5
    Thanks to everyone for contributing to this, it has saved me a bunch of time!
    Most everything worked for me aside from custom classes. I needed to show/hide a custom class with its own child properties and things weren't showing correctly so i modified it to traverse the child properties and show them as well as calculate their correct combined height.

    Im on 2020.3.3f1 and have included the fixes in post: https://forum.unity.com/threads/draw-a-field-only-if-a-condition-is-met.448855/#post-6947573

    This is the updated code for calculating the combined children property heights:

    Code (CSharp):
    1. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    2.     {
    3.           if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw) {
    4.               return -EditorGUIUtility.standardVerticalSpacing;
    5.         }
    6.         else
    7.         {
    8.             if(property.propertyType == SerializedPropertyType.Generic)
    9.             {
    10.                 int numChildren = 0;
    11.                 float totalHeight = 0.0f;
    12.  
    13.                 IEnumerator children = property.GetEnumerator();
    14.  
    15.                 while (children.MoveNext())
    16.                 {
    17.                     SerializedProperty child = children.Current as SerializedProperty;
    18.                  
    19.                     GUIContent childLabel = new GUIContent(child.displayName);
    20.  
    21.                     totalHeight += EditorGUI.GetPropertyHeight(child, childLabel) + EditorGUIUtility.standardVerticalSpacing;              
    22.                     numChildren ++;
    23.                 }
    24.  
    25.                 // Remove extra space at end, (we only want spaces between items)
    26.                 totalHeight -= EditorGUIUtility.standardVerticalSpacing;
    27.  
    28.                 return totalHeight;
    29.             }
    30.  
    31.             return EditorGUI.GetPropertyHeight(property, label);
    32.         }
    33.     }
    And here is the updated code for creating all the children properties:

    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2.     {
    3.         // If the condition is met, simply draw the field.
    4.         if (ShowMe(property))
    5.         {
    6.             // A Generic type means a custom class...
    7.             if(property.propertyType == SerializedPropertyType.Generic)
    8.             {
    9.                 IEnumerator children = property.GetEnumerator();
    10.  
    11.                 Rect offsetPosition = position;
    12.  
    13.                 while (children.MoveNext())
    14.                 {
    15.                     SerializedProperty child = children.Current as SerializedProperty;
    16.                  
    17.                     GUIContent childLabel = new GUIContent(child.displayName);
    18.  
    19.                     float childHeight = EditorGUI.GetPropertyHeight(child, childLabel);
    20.                     offsetPosition.height = childHeight;
    21.  
    22.                     EditorGUI.PropertyField(offsetPosition, child, childLabel);
    23.                  
    24.                     offsetPosition.y += childHeight + EditorGUIUtility.standardVerticalSpacing;
    25.                 }
    26.             }
    27.             else
    28.             {
    29.                 EditorGUI.PropertyField(position, property, label);
    30.             }
    31.  
    32.         } //...check if the disabling type is read only. If it is, draw it disabled
    33.         else if (drawIf.disablingType == DrawIfAttribute.DisablingType.ReadOnly)
    34.         {
    35.             GUI.enabled = false;
    36.             EditorGUI.PropertyField(position, property, label);
    37.             GUI.enabled = true;
    38.         }
    39.     }
    I hope this is helpful to someone...
    Thanks again!
     
    _TheFuture_, dmno, ExtraCat and 2 others like this.
  32. Fishmoun

    Fishmoun

    Joined:
    Jan 27, 2020
    Posts:
    10
    Thanx to everyone who contributed to this thread, you guys are awesome!
    I was working on a project and decided to add this fun feature to my editor, I ended up spending hours going through all of your code and I learned some really cool things I never knew.
     
  33. bhupiister

    bhupiister

    Joined:
    Dec 13, 2019
    Posts:
    42
    I am not an expert programmer, but i have moved my code more towards using scriptable objects and one way i found to draw list and array and hide them was to define list and other objects as scriptable object
    Code (CSharp):
    1. public enum PropertyType
    2. {
    3.     UILableBigU, UILableSmall, UILableSmallU, UIInputField, UIDropDown, UIButton
    4. }
    //My custom class
    Code (CSharp):
    1. [Serializable]
    2. public class Feature
    3. {
    4.     public string Lable;
    5.     [SerializeField] PropertyType propertyType = PropertyType.UIInputField;
    6.     [DrawIf("propertyType", PropertyType.UIInputField)]
    7.     public floatVariable Value;
    8.     [DrawIf("propertyType", PropertyType.UIDropDown)]
    9.     public listVariable list;
    10. }
    //listVariable.cs
    Code (CSharp):
    1. [CreateAssetMenu(fileName = "New Parameters", menuName = "CustomVariable/listVariable")]
    2. public class listVariable : ScriptableObject, ISerializationCallbackReceiver
    3. {
    4.  
    5.     public List<String> InitialValue;
    6.     [NonSerialized]
    7.     public List<String> Value;//Runtime value
    8.     public void OnAfterDeserialize()
    9.     {
    10.         Value = InitialValue;
    11.     }
    12.     public void OnBeforeSerialize() { }
    13.  
    14.     [TextArea]
    15.     public string Notes = "Comment Here.";
    16. }
    This method also helps if you accidentally resize array and loose data, because all your data is stored in scriptable object created.
     
  34. Marks4

    Marks4

    Joined:
    Feb 25, 2018
    Posts:
    547
    Ufol, Otnima, SuspecM and 4 others like this.
  35. vessago

    vessago

    Joined:
    Aug 2, 2018
    Posts:
    4
    Thanks, its awesome :)
     
  36. Ensortainment

    Ensortainment

    Joined:
    Dec 19, 2020
    Posts:
    15
    Thanks for this! Only change I really had to make was to add in "label" a couple places as shown above. Was having an issue where field value was showing, but not the variable name.
     
  37. _TheFuture_

    _TheFuture_

    Joined:
    Mar 11, 2016
    Posts:
    4
    The equivalent of the functionality described in this post is EnableIf / DisableIf in NaughtyAttributes, in case someone's also wondering.
     
    ssojyeti2, PanicEnsues and mikaelK like this.
  38. ExtraCat

    ExtraCat

    Joined:
    Aug 30, 2019
    Posts:
    52
    The DrawIf attribute worked great for a while, until I needed it to work with another CustomPropertyDrawer. That's when it stopped working, because it draws default-ish inspector instead of required custom drawer. I didn't feel like installing a bunch of different attributes from the link above only to get one single attribute working, so I found this. Works very similar to DrawIf, and also supports my case.
     
    Last edited: Mar 12, 2023
  39. zacharyruiz1

    zacharyruiz1

    Joined:
    Jan 4, 2021
    Posts:
    7
    Hey all, I realize that this is something of a necro, but I had an issue with classes or structs in lists being drawn outside of the list. You would see them in the list, and then drawn again as an independent field outside of the list. To fix this, I added a Hashset in the OnGUI and GetPropertyHeight methods. For each child property, it checks if the Hashset contains that property. If so, it skips rendering it/ adding it's height to the total height. Otherwise, it adds it to the hashset. The basic idea is that if it's already been rendered, we don't need to render it again. I will include pictures of the issue and the code below.

    After.png
    Before.png


    Code (CSharp):
    1. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    2.     {
    3.         if (!ShowMe(property) && drawIf.disablingType == DrawIfAttribute.DisablingType.DontDraw)
    4.         {
    5.             return -EditorGUIUtility.standardVerticalSpacing;
    6.         }
    7.         else
    8.         {
    9.             if (property.propertyType == SerializedPropertyType.Generic)
    10.             {
    11.                 int numChildren = 0;
    12.                 float totalHeight = 0.0f;
    13.  
    14.                 IEnumerator children = property.GetEnumerator();
    15.                 HashSet<SerializedProperty> drawnprops = new HashSet<SerializedProperty>();
    16.  
    17.                 while (children.MoveNext())
    18.                 {
    19.                     SerializedProperty child = children.Current as SerializedProperty;
    20.                     if (drawnprops.Contains(child))
    21.                     {
    22.                         continue;
    23.                     }
    24.                     drawnprops.Add(child);
    25.  
    26.                     GUIContent childLabel = new GUIContent(child.displayName);
    27.  
    28.                     totalHeight += EditorGUI.GetPropertyHeight(child, childLabel) + EditorGUIUtility.standardVerticalSpacing;
    29.                     numChildren++;
    30.                 }
    31.  
    32.                 // Remove extra space at end, (we only want spaces between items)
    33.                 totalHeight -= EditorGUIUtility.standardVerticalSpacing;
    34.  
    35.                 return totalHeight;
    36.             }
    37.  
    38.             return EditorGUI.GetPropertyHeight(property, label);
    39.         }
    40.     }
    41.  
    Code (CSharp):
    1.  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2.     {
    3.         // If the condition is met, simply draw the field.
    4.         if (ShowMe(property))
    5.         {
    6.             // A Generic type means a custom class...
    7.             if (property.propertyType == SerializedPropertyType.Generic)
    8.             {
    9.                 IEnumerator children = property.GetEnumerator();
    10.  
    11.                 Rect offsetPosition = position;
    12.                 HashSet<SerializedProperty> drawnprops = new HashSet<SerializedProperty>();
    13.          
    14.                 while (children.MoveNext())
    15.                 {
    16.                     SerializedProperty child = children.Current as SerializedProperty;
    17.                     if (drawnprops.Contains(child))
    18.                     {
    19.                         continue;
    20.                     }
    21.  
    22.                     GUIContent childLabel = new GUIContent(child.displayName);
    23.  
    24.                     float childHeight = EditorGUI.GetPropertyHeight(child, childLabel);
    25.                     offsetPosition.height = childHeight;
    26.  
    27.                     EditorGUI.PropertyField(offsetPosition, child, childLabel);
    28.  
    29.                     offsetPosition.y += childHeight + EditorGUIUtility.standardVerticalSpacing;
    30.                     drawnprops.Add(child);
    31.                 }
    32.             }
    33.             else
    34.             {
    35.                 EditorGUI.PropertyField(position, property, label);
    36.             }
    37.  
    38.         } //...check if the disabling type is read only. If it is, draw it disabled
    39.         else if (drawIf.disablingType == DrawIfAttribute.DisablingType.ReadOnly)
    40.         {
    41.             GUI.enabled = false;
    42.             EditorGUI.PropertyField(position, property, label);
    43.             GUI.enabled = true;
    44.         }
    45.     }
    46.  

    Hope this helps! I haven't tested it too thoroughly, you may run into issues if for some reason you DO intend to render a field more than once, though I don't know why or how you would do that anyway.