Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Any way to create Popup or List Box that allows multiple selections?

Discussion in 'Immediate Mode GUI (IMGUI)' started by Jeff_Georgeson, Jun 14, 2015.

  1. Jeff_Georgeson

    Jeff_Georgeson

    Joined:
    Apr 26, 2015
    Posts:
    18
    I need to create a popup (or list box-type control) in an editor window that allows the user to make multiple selections. I gather from other threads (such as http://answers.unity3d.com/questions/393992/custom-inspector-multi-select-enum-dropdown.html) that one can use an enum for this, but this seems to require that I enumerate not just every item in the list, but every possible combination of items in the list. I have (potentially) very long lists of as many as a hundred items, and trying to deal with every possible combination of those items would be extremely awkward. (The lists are generated from previous user input as well, so the enum would have to happen dynamically.)

    I also thought about using a button for each member of the list, but because of the potential length that creates far too much clutter.

    Am I missing some obvious implementation of such a multiple-selection list box? Or am I incorrect about the way the enum would have to work? (Or, ultimately, is there any other editing system in the works so I'm not having to use the ancient GUI system to create editor extensions?)

    Thanks for any help!
     
    John-Azar likes this.
  2. mambo_2

    mambo_2

    Joined:
    Aug 19, 2013
    Posts:
    19
    Not sure what your exact problem is, wither it's a popup menu in the final build as an interactable UI or a Custom inspector for your plugin users but if it's the later try using this,

    EditorGUI.Popup this will help you specify the list to portray and the index you're on in that list, to make sure the value stays the same you can do something like this in your drawer,

    Code (CSharp):
    1.  private bool start = true;
    2.  
    3. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    4.     {
    5. if (start)
    6.         {
    7.             start = false;
    8.             for (int i = 0; i < list.length; i++)
    9.             {
    10.                 if (list[i] == property.stringValue)
    11.                 {
    12.                    "Index in your attribute extended class" = i;
    13.                 }
    14.             }
    15.         }
    16.  
    17.         "Index in your attribute extended class"  = EditorGUI.Popup(position,property.name,  "Index in your attribute extended class" , list);
    18.         property.stringValue = list["Index in your attribute extended class"];
    19. }
    20.  
     
  3. Jeff_Georgeson

    Jeff_Georgeson

    Joined:
    Apr 26, 2015
    Posts:
    18
    Thanks for your reply! I'm using it in an EditorWindow as part of an asset I'm building. Currently I'm just using the normal EditorGUILayout.Popup, for instance like this:

    Code (CSharp):
    1. selectedStory = EditorGUILayout.Popup("", selectedStory, storyList, GUILayout.Width (200));
    where selectedStory is the int index and storyList an array of strings.

    This of course makes my users select one item, click "Add", click to open the popup again and select another item, click "Add", etc. It works, but can be very tedious if you have several items to add.

    I'm not very familiar with property drawers, so forgive me if I'm missing something, but does your code keep multiple items in the EditorGUI.Popup checked/selected? It appears that "Index in your attribute extended class" is still a single index, and the popup will still just show one item selected at a time. Or is there some way that it is modifying the actual EditorGUI.Popup so that it takes an array (or something) as the index position?
     
  4. mambo_2

    mambo_2

    Joined:
    Aug 19, 2013
    Posts:
    19


    Oh, I misunderstood your question then. I thought you wanted a single selection popup list, sadly thought I can not help you with what you are looking for at the moment as I never tried it myself, I will look up into it as soon as I have some free time, as for my code it checks what the saved value in the property is so that when you change the values in the array the value of that property stays the same.
     
  5. KramerZhang

    KramerZhang

    Joined:
    Jul 25, 2014
    Posts:
    3
    There is a tricky way to do this. add the slash "/" to the options of EditorGUILayout.Popup.

    Code (CSharp):
    1. EditorGUILayout.Popup(index, ["content / with slash"]);
    1161331689.jpg
     
    Hello_hxh likes this.
  6. Jeff_Georgeson

    Jeff_Georgeson

    Joined:
    Apr 26, 2015
    Posts:
    18
    Thanks for the info! That's still not what I'm asking, but it's very cool to know that (and will help with some of the long alpha lists I'm creating). How do you keep the slash character from showing up in the main list? My attempt keeps showing the slash in the main popup menu along with the first option of the submenu. Can you show me the code behind your menu jpg?

    What I'm trying to do is allow multiple simultaneous selections, such as being able to select A, B, and C in a popup list (instead of the default behavior, which is to only allow one choice, such as A or B or C, but not more than one of these).
     
  7. davidosullivan

    davidosullivan

    Joined:
    Jun 9, 2015
    Posts:
    387
    I'd really like to know what the code for that is too...
     
  8. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    http://docs.unity3d.com/ScriptReference/EditorGUI.MaskField.html

    If you need strings you need to convert the int back in to your options but nothing too tricky there:


    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections.Generic;
    5.  
    6. public class GUITest : EditorWindow {
    7.     [MenuItem("Examples/Mask Field Usage")]
    8.     static void Init() {
    9.         GUITest window = GetWindow<GUITest> ();
    10.         window.Show();
    11.     }
    12.  
    13.     int flags = 0;
    14.     string[] options = new string[]{"CanJump", "CanShoot", "CanSwim"};
    15.     void OnGUI() {
    16.         flags = EditorGUILayout.MaskField ("Player Flags", flags, options);
    17.         List<string> selectedOptions = new List<string>();
    18.         for (int i = 0; i < options.Length; i++)
    19.         {
    20.             if ((flags & (1 << i )) == (1 << i) ) selectedOptions.Add(options[i]);
    21.         }
    22.         if (GUILayout.Button ("Print Options"))
    23.         {
    24.             foreach (string o in selectedOptions) Debug.Log (o);
    25.         }
    26.     }
    27. }
    28.  
    29.  
    (This is the Unity sample modified with extra function to turn to string .. typed in the window so may have errors, hopefully intent is clear)

    EDIT: Of course you don't have to create a new string list everytime, for example you could update only if flags is different to previous value.
     
    Last edited: Dec 1, 2015
  9. Ramsis6

    Ramsis6

    Joined:
    Nov 16, 2017
    Posts:
    1
    If I understood your question well, i think you should create an array of indexes instead of only one index for all the members. for example
    Code (CSharp):
    1. for (int i = 0; i < members.Length; i++)
    2.         {
    3.             indexes[i] = EditorGUILayout.Popup("Select item", indexes[i], options);
    4.            
    5.         }
     
  10. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,205
  11. a-t-hellboy

    a-t-hellboy

    Joined:
    Dec 6, 2013
    Posts:
    180
    Hey @karl_jones,
    Could you provide any link, piece of code or anything helpful which how I can use GenericMenu for multi selection dropdown menu ? (I've made dropdown menu with GenericMenu before)
     
    MostHated likes this.
  12. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    714
    Hey, I was digging into this and found this thread. I used the GenericMenu api to make what OP was originally asking for!

    Code (CSharp):
    1.  
    2.     private List<int> Selected = new List<int>();
    3.  
    4.     void OnPointSelected(object index)
    5.     {
    6.         var intIndex = (int) index;
    7.  
    8.         if(Selected.Contains(intIndex))
    9.         {
    10.             Selected.Remove(intIndex);
    11.         }
    12.         else
    13.         {
    14.             Selected.Add(intIndex);
    15.         }
    16.     }
    17.  
    18.     private void DrawPointSelectorInspector(SomeData[] data)
    19.     {
    20.         var selectedPointButtonSb = new System.Text.StringBuilder();
    21.  
    22.         if (Selected.Count == 0)
    23.         {
    24.             selectedPointButtonSb.Append("No points selected.");
    25.         }
    26.         else
    27.         {
    28.             foreach (var i in Selected)
    29.             {
    30.                 selectedPointButtonSb.Append($"{i:N0}, ");
    31.             }
    32.         }
    33.  
    34.         if (GUILayout.Button(selectedPointButtonSb.ToString()))
    35.         {
    36.             var selectedMenu = new GenericMenu();
    37.  
    38.             for (var i = 0; i < data.Length; ++i)
    39.             {
    40.                 var menuString = $"{i:N0}";
    41.                 var selected = Selected.Contains(i);
    42.                 selectedMenu.AddItem(new GUIContent(menuString), selected, OnPointSelected, i);
    43.             }
    44.  
    45.             selectedMenu.ShowAsContext();
    46.         }
    47.     }