Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Enum Flags as Toggle Buttons

Discussion in 'Scripting' started by Democide, Feb 17, 2015.

  1. Democide

    Democide

    Joined:
    Jan 29, 2013
    Posts:
    315
    So like many before me, I wanted enum flags available in the editor. The best solutions I found were using a custom Attribute and PropertyDrawer to make it look like similar to the LayerMask dropdown. While that's neat, it comes with the disadvantage that you have no real overview once multiple flags are set. You need to open the dropdown to see the actual state.

    That didn't work for me, so I built a solution using Toggle Buttons:



    You can get the code from here: http://www.sharkbombs.com/2015/02/17/unity-editor-enum-flags-as-toggle-buttons/

    And: If anyone has any useful feedback, I'd love to hear it!

    Oh and: I've uploaded an upgraded version of this to the asset store here:
    https://www.assetstore.unity3d.com/#!/content/74356
     
    Last edited: Jan 23, 2018
    dyupa and domkia like this.
  2. xenius

    xenius

    Joined:
    Sep 30, 2010
    Posts:
    523
    Hi @Martin Sharkbomb
    I just tried popping this in, and I get a single row of absolutelly tiny buttons. Is there a trick to getting 3 nicely formatted rows as you have in the gif above? Thanks!
     
  3. Democide

    Democide

    Joined:
    Jan 29, 2013
    Posts:
    315
    These are three separate enums. The issue is, that you have an enum with a lot of options. There's currently no code that detects this and does any linebreaks. I didn't need it and didn't really want to bother. It's not terribly difficult but I just was done with it at that point.

    If you do upgrade it to contain that functionality, let me know though. I'd be happy to update the script.
     
    xenius likes this.
  4. xenius

    xenius

    Joined:
    Sep 30, 2010
    Posts:
    523
    Hey Martin, Got some help from a friend today modifying it. Here's the new code for the EnumFlagDrawer.cs:


    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(EnumFlagAttribute))]
    6. public class EnumFlagsAttributeDrawer : PropertyDrawer
    7. {
    8.     const float mininumWidth = 80.0f;
    9.  
    10.     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    11.     {
    12.         int buttonsIntValue = 0;
    13.         int enumLength = _property.enumNames.Length;
    14.  
    15.         float enumWidth = (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - 30);
    16.  
    17.         int buttonsPerRow = Mathf.FloorToInt(enumWidth / mininumWidth);
    18.         int numRows = Mathf.CeilToInt((float)enumLength / (float)buttonsPerRow);
    19.  
    20.        
    21.         bool[] buttonPressed = new bool[enumLength];
    22.  
    23.         float buttonWidth = enumWidth / Mathf.Min(buttonsPerRow, enumLength);
    24.  
    25.         EditorGUI.LabelField(new Rect(_position.x, _position.y, EditorGUIUtility.labelWidth, _position.height), _label);
    26.  
    27.         EditorGUI.BeginChangeCheck();
    28.  
    29.        
    30.         for (int row = 0; row < numRows; row++)
    31.         {
    32.             for (int button = 0; button < buttonsPerRow; button++)
    33.             {
    34.                 int i = button + row * buttonsPerRow;
    35.  
    36.                 if (i >= enumLength) { break; }
    37.  
    38.                 // Check if the button is/was pressed
    39.                 if ((_property.intValue & (1 << i)) == 1 << i)
    40.                 {
    41.                     buttonPressed[i] = true;
    42.                 }
    43.  
    44.                 Rect buttonPos = new Rect(_position.x + EditorGUIUtility.labelWidth + buttonWidth * button, _position.y + row * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing), buttonWidth, EditorGUIUtility.singleLineHeight);
    45.  
    46.                 buttonPressed[i] = GUI.Toggle(buttonPos, buttonPressed[i], _property.enumNames[i], EditorStyles.toolbarButton);
    47.  
    48.                 if (buttonPressed[i])
    49.                     buttonsIntValue += 1 << i;
    50.             }
    51.         }
    52.  
    53.         if (EditorGUI.EndChangeCheck())
    54.         {
    55.             _property.intValue = buttonsIntValue;
    56.         }
    57.     }
    58.    
    59.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    60.     {
    61.         int enumLength = property.enumNames.Length;
    62.  
    63.         float enumWidth = (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - 30);
    64.  
    65.         int buttonsPerRow = Mathf.FloorToInt(enumWidth / mininumWidth);
    66.         int numRows = Mathf.CeilToInt((float)enumLength / (float)buttonsPerRow);
    67.  
    68.  
    69.         return numRows * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
    70.     }
    71. }
     
    Democide likes this.
  5. Democide

    Democide

    Joined:
    Jan 29, 2013
    Posts:
    315
    Sweet. I cleaned it up some more.

    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomPropertyDrawer(typeof(EnumFlagAttribute))]
    6. public class EnumFlagsAttributeDrawer : PropertyDrawer
    7. {
    8.     const float mininumWidth = 60.0f;
    9.  
    10.     int enumLength;
    11.     float enumWidth;
    12.  
    13.     int numBtns;
    14.     int numRows;
    15.  
    16.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    17.     {
    18.         SetDimensions(property);
    19.         return numRows * EditorGUIUtility.singleLineHeight + (numRows - 1) * EditorGUIUtility.standardVerticalSpacing;
    20.     }
    21.  
    22.     void SetDimensions(SerializedProperty property) {
    23.         enumLength = property.enumNames.Length;
    24.         enumWidth = (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - 20 );
    25.  
    26.         numBtns = Mathf.FloorToInt(enumWidth / mininumWidth);
    27.         numRows = Mathf.CeilToInt((float)enumLength / (float)numBtns);
    28.     }
    29.  
    30.     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    31.     {
    32.         SetDimensions(_property);
    33.  
    34.         int buttonsIntValue = 0;
    35.         bool[] buttonPressed = new bool[enumLength];
    36.         float buttonWidth = enumWidth / Mathf.Min(numBtns, enumLength);
    37.  
    38.         EditorGUI.LabelField(new Rect(_position.x, _position.y, EditorGUIUtility.labelWidth, _position.height), _label);
    39.  
    40.         EditorGUI.BeginChangeCheck ();
    41.  
    42.         for (int row = 0; row < numRows; row++) {
    43.             for (int btn = 0; btn < numBtns; btn++) {
    44.                 int i = btn + row * numBtns;
    45.  
    46.                 if (i >= enumLength) {
    47.                     break;
    48.                 }
    49.  
    50.                 // Check if the button is/was pressed
    51.                 if ((_property.intValue & (1 << i)) == 1 << i) {
    52.                     buttonPressed[i] = true;
    53.                 }
    54.  
    55.                 Rect buttonPos = new Rect(_position.x + EditorGUIUtility.labelWidth + buttonWidth * btn, _position.y + row * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing), buttonWidth, EditorGUIUtility.singleLineHeight);
    56.                 buttonPressed[i] = GUI.Toggle(buttonPos, buttonPressed[i], _property.enumNames[i], EditorStyles.toolbarButton);
    57.  
    58.                 if (buttonPressed[i])
    59.                     buttonsIntValue += 1 << i;
    60.             }
    61.         }
    62.  
    63.         if (EditorGUI.EndChangeCheck()) {
    64.             _property.intValue = buttonsIntValue;
    65.         }
    66.     }
    67. }
    68.  
     
    xenius likes this.
  6. Android272

    Android272

    Joined:
    Oct 14, 2016
    Posts:
    25
    This is fantastic, exactly what I was looking for. I know that it has been a while but. Do you have any idea why I am getting a
    type is not a enum value
    in the
    SetDimensions
    at these two lines?

    Code (CSharp):
    1.  
    2. enumLength = property.enumNames.Length;
    3. enumWidth = (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - 20 );
    4.  

    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEngine;
    4. [CustomPropertyDrawer(typeof(EnumFlagAttribute))]
    5. public class EnumFlagsAttributeDrawer : PropertyDrawer
    6. {
    7.     const float mininumWidth = 60.0f;
    8.     int enumLength;
    9.     float enumWidth;
    10.     int numBtns;
    11.     int numRows;
    12.    
    13.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    14.     {
    15.         SetDimensions(property);
    16.         return numRows * EditorGUIUtility.singleLineHeight + (numRows - 1) * EditorGUIUtility.standardVerticalSpacing;
    17.     }
    18.     void SetDimensions(SerializedProperty property) {
    19.         enumLength = property.enumNames.Length;
    20.         enumWidth = (EditorGUIUtility.currentViewWidth - EditorGUIUtility.labelWidth - 20 );
    21.         numBtns = Mathf.FloorToInt(enumWidth / mininumWidth);
    22.         numRows = Mathf.CeilToInt((float)enumLength / (float)numBtns);
    23.     }
    24.     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    25.     {
    26.         SetDimensions(_property);
    27.         int buttonsIntValue = 0;
    28.         bool[] buttonPressed = new bool[enumLength];
    29.         float buttonWidth = enumWidth / Mathf.Min(numBtns, enumLength);
    30.         EditorGUI.LabelField(new Rect(_position.x, _position.y, EditorGUIUtility.labelWidth, _position.height), _label);
    31.         EditorGUI.BeginChangeCheck ();
    32.         for (int row = 0; row < numRows; row++) {
    33.             for (int btn = 0; btn < numBtns; btn++) {
    34.                 int i = btn + row * numBtns;
    35.                 if (i >= enumLength) {
    36.                     break;
    37.                 }
    38.                 // Check if the button is/was pressed
    39.                 if ((_property.intValue & (1 << i)) == 1 << i) {
    40.                     buttonPressed[i] = true;
    41.                 }
    42.                 Rect buttonPos = new Rect(_position.x + EditorGUIUtility.labelWidth + buttonWidth * btn, _position.y + row * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing), buttonWidth, EditorGUIUtility.singleLineHeight);
    43.                 buttonPressed[i] = GUI.Toggle(buttonPos, buttonPressed[i], _property.enumNames[i], EditorStyles.toolbarButton);
    44.                 if (buttonPressed[i])
    45.                     buttonsIntValue += 1 << i;
    46.             }
    47.         }
    48.         if (EditorGUI.EndChangeCheck()) {
    49.             _property.intValue = buttonsIntValue;
    50.         }
    51.     }
    52. }
     
  7. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    Please show your usage of this attribute as well.
    Make sure you put it on the Enum field, marked with [Flags] attribute
     
  8. Android272

    Android272

    Joined:
    Oct 14, 2016
    Posts:
    25
    Here is my enum

    Code (CSharp):
    1. public enum EPlayMode { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }
    Here is where I am trying to access the data.

    Code (CSharp):
    1. public class Object : MonoBehaviour
    2. {
    3.     [EnumFlag]     public EPlayMode test;
    4.    
    5.     private void Update() {
    6.         Debug.Log("a" + !test.Equals(EPlayMode.FirstOnly));
    7.         Debug.Log("b" + !test.Equals(0));
    8.     }
    9. }
    Eventually, I would like to write if statements that check what values are selected and do something specific depending on what is selected. What I am want to do with this is have a bunch of different ambient sounds that I can select on an object. If crows are selected then you will hear crows, if crows and windy trees are selected then you will hear crows and trees blowing in the wind. This may not be the best way of doing this architecturally but its the idea that is in my head.
     
  9. Deadcow_

    Deadcow_

    Joined:
    Mar 13, 2014
    Posts:
    135
    Try to add [Flags] attribute to enum, like this

    Code (CSharp):
    1. [Flags]
    2. public enum EPlayMode { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z }
     
  10. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    Whatever the problem is in the original post, this is (presumably) not going to fix it, because a bit field enumeration behaves differently than a constant value enumeration. You an find more about this in the official Microsoft Docs, under FlagsAttribute Class. There are also examples and guidelines on how to use this attribute.

    EDIT: Looked a bit deeper into the posted code, yes, you are right, they use enumeration as a bit field, so the attribute should be used.

    Other than that, it is a great way to achieve OP's need, in particular this:
    Things like Enum.HasFlag(Enum) Method (Microsoft Docs) are very handy in this case.
     
    Last edited: Dec 19, 2019