Search Unity

Reorderable List v2

Discussion in 'Immediate Mode GUI (IMGUI)' started by CDF, Jul 9, 2015.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Hi, just wanted to share some work I've been doing on a Reorderable List.

    Moved to Github: https://github.com/cfoulston/Unity-Reorderable-List

    It's an attempt to mimic functionality of the ReorderableList within Unity while adding some extended functionality.

     
    Last edited: Apr 12, 2017
    mysticfall, Trisibo, Aseemy and 4 others like this.
  2. AhrenM

    AhrenM

    Joined:
    Aug 30, 2014
    Posts:
    74
    How would this go with nested list structures. Say trees up to 5-7 levels deep?
     
  3. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    It should work the same as a 5-7 level deep array, you'll just need someway to define these nested arrays as Reorderable Lists. e.g:

    Code (CSharp):
    1. public class Example : MonoBehaviour {
    2.  
    3.     [Reorderable]
    4.     public ExampleChildList list;
    5.  
    6.     [System.Serializable]
    7.     public class ExampleChild {
    8.  
    9.         [Reorderable]
    10.         public ExampleChildList nested;
    11.     }
    12.  
    13.     [System.Serializable]
    14.     public class ExampleChildList : ReorderableArray<ExampleChild> {
    15.     }
    16. }
    This code will however throw a SerializationDepth error as there is a possibility of infinite nested lists.
     
  4. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I'm still trying to figure out a better way to define these lists using Attributes... Shame you can't define an attribute for the entire array. And it's not possible to write a propertyDrawer for a generic type.
     
  5. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Ok, I think I've got a better solution to the whole reference issue.

    instead of referencing by propertyPath, the list generates an id (GetHashCode), the ReorderableArray then stores this id as a private serializedProperty. The reference to the list is acquired using this id, if it doesn't exist, make a new list, otherwise return the existing one. It's working so far, selection is maintained.

    Also added optional sliding to the dragging.
     
  6. Roland1234

    Roland1234

    Joined:
    Nov 21, 2012
    Posts:
    190
    What up CDF? It looks like we've been thinking along the exact same lines. Eerily so. We should look into each other's implementations and see if we can't collaborate in some way. For now (because I'm about to pass out) imma drop me link. Hope you don't mind:
    http://forum.unity3d.com/threads/reordablelist-wip.351266/
    Cheers mate!
     
  7. Roland1234

    Roland1234

    Joined:
    Nov 21, 2012
    Posts:
    190
    I'm surprised we didn't join on the same date as well, because now we have the same number of messages :p
     
  8. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Cool, yeah I'll take a look. I'm really busy with Work right now, but hopefully there's something in both that people can benefit from
     
  9. MsPhilipFry

    MsPhilipFry

    Joined:
    Feb 28, 2014
    Posts:
    21
    1. HELP

    2. NullReferenceException: (null)
    3. UnityEditor.SerializedObject..ctor (UnityEngine.Object[] objs) (at C:/buildslave/unity/build/artifacts/generated/common/editor/SerializedPropertyBindings.gen.cs:74)
    4. UnityEditor.Editor.GetSerializedObjectInternal () (at C:/buildslave/unity/build/artifacts/generated/common/editor/EditorBindings.gen.cs:154)
    5. UnityEditor.Editor.get_serializedObject () (at C:/buildslave/unity/build/artifacts/generated/common/editor/EditorBindings.gen.cs:147)
    6. TestEditor.OnEnable () (at Assets/Editor/TestEditor.cs:275)
     
  10. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Will need to see the code of TestEditor.cs in order to help.
     
  11. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    CDF, where can I change the code so that instead of saying 'Element 0, Element 1, etc' it display custom strings like in your case 'My Kewl Element'?
     
  12. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    set the value of "elementNameProperty" to a property of your objects in the list

    Code (CSharp):
    1. list = new ReorderableList(mySerializedObject.FindProperty("myList"));
    2. list.elementNameProperty = "myCustomField"
    3.  
    4. class Container {
    5.  
    6.     [SerializeField]
    7.     private Child[] myList;
    8. }
    9.  
    10. [Serializable]
    11. class Child {
    12.  
    13.     public string myCustomField = "Hello";
    14. }
     
  13. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Ye, I can't quite get it to work, code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using Malee.Editor;
    5.  
    6. [CanEditMultipleObjects]
    7. [CustomEditor(typeof(Example))]
    8. public class ExampleEditor : Editor
    9. {
    10.  
    11.     private ReorderableList RotationTimeline;
    12.  
    13.     class Container {
    14.  
    15.     [SerializeField]
    16.     private Child[] RotationTimeline;
    17. }
    18.  
    19.  
    20. [System.Serializable]
    21. class Child {
    22.  
    23.     public string myCustomField = "Hello";
    24. }
    25.  
    26.  
    27.     void OnEnable() {
    28.  
    29.         RotationTimeline = new ReorderableList(serializedObject.FindProperty("RotationTimeline"));
    30.         RotationTimeline.elementNameProperty = "myCustomField";
    31.     }
    32.  
    33.     public override void OnInspectorGUI() {
    34.  
    35.         serializedObject.Update();
    36.  
    37.         //draw the list using GUILayout, you can of course specify your own position and label
    38.         RotationTimeline.DoLayoutList();
    39.  
    40.         //Caching the property is recommended
    41.  
    42.         //Still works, but there are some minor issues with selection state as properties can only be referenced by propertyPath
    43.         //And if that propertyPath changes (by array modification) then the list will be either rebuilt or point to a different reference
    44.         //EditorGUILayout.PropertyField(serializedObject.FindProperty("list2"));
    45.  
    46.         serializedObject.ApplyModifiedProperties();
    47.     }
    48. }
    49.  
     
  14. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    RotationTimeline.elementNameProperty="myCustomField";

    This is just saying, set the display of each element in the list to the value of "myCustomField" on each element.
    So the elements in the array need to have this property defined.
     
  15. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Got it to work, thanks! And another question if I'm not bothering you, how can I access a variable in the list? So in another script for example, how do I access the value of 'InitialAngle'?

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Malee;
    5.  
    6. public class Example : MonoBehaviour {
    7.  
    8.     public List<ExampleChild> RotationTimeline;
    9.  
    10.  
    11.     //[Reorderable]
    12. //    public ExampleChildList list2;
    13.  
    14.     [System.Serializable]
    15.     public class ExampleChild
    16.     {
    17.  
    18.         public string SingleRotation;
    19.         public string LoopedRotation;
    20.  
    21. //VAriables in each element
    22. //Just for showing, RotationType is used nowhere
    23.         public enum TypeOfRotation
    24.         {
    25.                     SingleRotation,
    26.                     LoopedRotation,
    27.         }
    28.         public TypeOfRotation RotationType;
    29.  
    30.  
    31.         public float InitialAngle;
    32.         public float FinalAngle;
    33.         public float Speed;
    34.  
    35.     }
    36.  
    37.     class Container {
    38.  
    39.     [SerializeField]
    40.     private ExampleChild[] RotationTimeline;
    41. }
    42.  
    43.     [System.Serializable]
    44.     public class ExampleChildList : ReorderableArray<ExampleChild> {
    45.     }
    46. }
    47.  
     
    Last edited: Dec 22, 2016
  16. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Code (CSharp):
    1. RotationTimeline = new ReorderableList(serializedObject.FindProperty("RotationTimeline"));
    2. //Get the first item in the list, if there are no items this will result in an error
    3. RotationTimeline.GetItem(0).FindPropertyRelative("InitialAngle").floatValue;
    At some point I plan to document the class, soon...
     
  17. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    and..

    I have this enum in one script:

    Code (CSharp):
    1.    public enum TypeOfRotation
    2.          {
    3.                      SingleRotation,
    4.                      LoopedRotation,
    5.          }
    6.          public TypeOfRotation RotationType;
    And then in another script I want set the value of elementNameProperty to be whatever was selected in the enum (SingleRotation or LoopedRotation). The following code doesn't work and I don't quite know how to achieve this.

    Code (CSharp):
    1.  RotationTimeline.elementNameProperty = RotationTimeline.GetItem(0).FindPropertyRelative("TypeOfRotation").enumNames;
    Merry christmas
     
  18. Cippman

    Cippman

    Joined:
    Sep 4, 2014
    Posts:
    12
    CDF you win!! Loveable package!! I hope to see that even on asset store (or in unity by default ahah) a thing like that!!
     
  19. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Merry Christmas alexanderameye I've updated the package with some name features. Hopefully they will work for your needs. Check the example for using an enum as the display name.

    In your example above, the code would simply be: RotationTimeline.elementNameProperty = "RotationType"
     
    alexanderameye likes this.
  20. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Thanks :D.

    Unfortunately I can't really support this full time because of other work, but sure I'd love to see this in Unity ;)
     
  21. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Hey CDF thank you so much for the update it's awesome!!

    to get back on the GetItem function. Is there a way to access the variables from the list in another non-editor script? Because I can get it to work, but only in editor scripts where I can put 'using Malee.Editor'.
     
  22. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Unfortunately, without reflection I don't think that's possible. Due to the way Unity compiles assemblies.

    What variables do you want to change from a non editor script?

    you could write an editor for a class that looks for changes and updates the ReorderableList variables on behalf.
    something like:
    Code (CSharp):
    1. public class MyClass : MonoBehaviour {
    2.  
    3.     [SerializeField]
    4.     private GameObject[] testList;
    5.  
    6.     [SerializeField]
    7.     private ListData listData = new ListData();
    8.  
    9.     void Update() {
    10.  
    11.         if (Time.time > 3) {
    12.  
    13.             listData.draggable = true;
    14.         }
    15.     }
    16.  
    17.     [System.Serializable]
    18.     internal class ListData {
    19.      
    20.         internal bool draggable = false;
    21.     }
    22. }
    23.  
    24.  
    25. [CustomEditor(typeof(MyClass))]
    26. public class MyEditor : Editor {
    27.  
    28.     private ReorderableList list;
    29.     private serializedProperty listData;
    30.  
    31.     void OnEnable() {
    32.  
    33.         list = new ReorderableList(serializedObject.FindProperty("testList"));
    34.         listData = serializedObject.FindProperty("listData");
    35.     }
    36.  
    37.     public override OnInspectorGUI() {
    38.  
    39.         serializedObject.Update();
    40.  
    41.         list.draggable = listData.FindPropertyRelative("draggable").boolValue;
    42.         list.DoLayoutList();
    43.  
    44.         serializedObject.ApplyModifiedProperties();
    45.     }
    46. }
    Untested code, but hopefully you get the idea
     
  23. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    I'll take a look at your code. In more detail, I want to access the variables in my list from another (non editor script) and use those values. I don't want to modify the values, but just read-only. So for example in a script attached to a gameobject, there is a variable that has the value of whatever is inputted in the list from the editor script before running the game.
     
  24. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    You can just access the array as usual on your non editor script.
     
  25. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    I got it to work! I owe you so much :) more questions (im' sorry) might be coming but for now I'm good. Awesome package!
     
  26. Deleted User

    Deleted User

    Guest

  27. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    hey CDF, me again. Is there a way to change the default values of variables in the list. So that when the user attaches the script to an object, and adds a list element, some variables already have default values. (without using a prefab), thanks! And also I noticed, when I enter values in a list element, and then I add a new element, it copies the variable values of the previous list, is there a way to modify this?
     
  28. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    That's just how Unity's serialization system works. Unless your objects derive from MonoBehaviour or ScriptableObject those items will initialize to their base default values: float = 0, bool = false etc.
    When duplicating an element, Unity will copy the serialized properties into the new object automatically.

    You can see this happening on a regular array. Right click the element in the Editor and Duplicate it, notice the values are copied.

    You can listen to the "onAddCallback" on the List to override the "add" button functionality.

    In order to initialize a complete list on Startup you would do something similar. Make a private "initialized" bool on your class. Check the value on the CustomEditor "OnInspectorGUI" method, if false, make it true. Use list.AddItem() as above and initialize the default values. Something like:

    Code (CSharp):
    1. class MyClass {
    2.  
    3.     [SerializeField]
    4.     private bool initialized;
    5.  
    6.     public MyItem[] items;
    7.  
    8.     [System.Serializable]
    9.     public class MyItem {
    10.  
    11.         public bool myBool;
    12.         public float myFloat;
    13.     }
    14. }
    15.  
    16. class MyClassEditor : Editor {
    17.  
    18.     void OnEnable() {
    19.  
    20.         list = new ReorderableList(serializedObject.FindProperty("items"));
    21.         list.onAddCallback += AddItemToList;
    22.     }
    23.  
    24.     public override OnInspectorGUI() {
    25.  
    26.         serializedObject.Update();
    27.  
    28.         if (!serializedObject.FindProperty("initialized").boolValue) {
    29.  
    30.             serializedObject.FindProperty("initialized").boolValue = true;
    31.  
    32.             InitializeItems();
    33.         }
    34.  
    35.         serializedObject.ApplyModifiedProperties();
    36.     }
    37.  
    38.     void AddItemToList(ReorderableList list) {
    39.  
    40.         InitializeItem(list.AddItem());
    41.     }
    42.  
    43.     private void InitializeItems() {
    44.  
    45.         for (int i = 0; i < 10; i++) {
    46.  
    47.             InitializeItem(list.AddItem());
    48.         }
    49.     }
    50.  
    51.     private void InitializeItem(SerializedProperty item) {
    52.  
    53.         item.FindPropertyRelative("myBool").boolValue = true;
    54.         item.FindPropertyRelative("myFloat").floatValue = 1;
    55.     }
    56. }
    I don't have a callback for Duplicate yet though. I'm pretty busy now, if you need it, check "
    HandleContextClick" and "HandleMultipleContextClick" functions. You should be able to hook up a callback in there with the right data
     
    Last edited: Mar 3, 2017
  29. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Also worth mentioning is you could skip the initialization through the list by directly modifying the array. You'll still need an "initialized" bool. This way, you won't generate an Undo/Redo on the items.
     
  30. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Thanks! I have one last (I hope) question though. I want to draw some variables outside of list in the inspector window. This is the code for List.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Malee;
    5.  
    6. public class List : MonoBehaviour
    7. {
    8.     public float x;
    9.     public List<RotationTimelineData> RotationTimeline; //Define a list.
    And this is the code in ListEditor.cs

    Code (CSharp):
    1. public override void OnInspectorGUI()
    2.     {
    3.         DrawDefaultInspector();
    4.         serializedObject.Update();
    5.         RotationTimeline.DoLayoutList();
    6.         serializedObject.ApplyModifiedProperties();
    7.     }
    This draws the variable I want and the list, but also the list in the form of an array:



    Any fixes you could think of?
     
  31. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    UPDATE:

    Is this a correct way to achieve what I want?

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4. using Malee.Editor;
    5.  
    6. [CanEditMultipleObjects]
    7. [CustomEditor(typeof(List))]
    8. public class ListEditor : Editor
    9. {
    10.     private ReorderableList RotationTimeline;
    11.    
    12.      public float damage;
    13.     void OnEnable()
    14.     {
    15.         RotationTimeline = new ReorderableList(serializedObject.FindProperty("RotationTimeline"));
    16.         RotationTimeline.elementNameProperty = "RotationType";
    17.     }
    18.  
    19.     public override void OnInspectorGUI()
    20.     {
    21.         damage = EditorGUILayout.FloatField("Damage", damage);
    22.  
    23.         serializedObject.Update();
    24.         RotationTimeline.DoLayoutList();
    25.         serializedObject.ApplyModifiedProperties();
    26.     }
    27. }
    28.  
     
  32. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I'd use the seralized property instead
    Code (CSharp):
    1. public override void OnInspectorGUI()
    2.     {
    3.         //damage = EditorGUILayout.FloatField("Damage", damage);
    4.         serializedObject.Update();
    5.  
    6.        EditorGUILayout.PropertyField(serializedObject.FindProperty("damage"));
    7.  
    8.         RotationTimeline.DoLayoutList();
    9.         serializedObject.ApplyModifiedProperties();
    10.     }
     
  33. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Works nicely! This way I can more easily access the damage variable in my List.cs (non-editor) script. Have a great day!
     
  34. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Hey CDF, how would I code it so that the variables in a list element are shown or not based on a toggle in that same list element.

    So for regular variables outside of the list I would do (in OnInspectorGUI)

    Code (CSharp):
    1. if(DoorPro.VisualizeHinge == true)
    2.         {
    3.             EditorGUILayout.PropertyField(serializedObject.FindProperty("HingeType"));
    4.             EditorGUILayout.PropertyField(serializedObject.FindProperty("HingeColor"));
    5.         }
    so the variables HingeType and HingeColor would only show when VisualizeHinge is toggle to true. But can I achieve the same inside a list element?
     
  35. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Use the "drawElementCallback" to draw the property and its children. You may want to use the "getElementHeightCallback" to return a different height for each element if you're changing how properties are shown.
     
  36. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Could you give an example? I'm having trouble implementing this...
     
  37. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Something like this perhaps:

    Code (CSharp):
    1. void OnEnable() {
    2.  
    3.     list = new ReorderableList(serializedObject.FindProperty("MyList"));
    4.     list.drawElementCallback += DrawElement;
    5.     list.getElementHeightCallback += GetElementHeight;
    6. }
    7.  
    8. void DrawElement(Rect rect, SerializedProperty element, GUIContent label, bool selected, bool focused) {
    9.  
    10.     if (DoorPro.VisualizeHinge == false) {
    11.  
    12.         switch (element.name) {
    13.  
    14.             case "HingeType":
    15.             case "HingeColor":
    16.  
    17.                 //don't draw this element if VisualizeHinge is false and the element is either "HingeType" or "HingeColor"
    18.                 return;
    19.         }
    20.     }
    21.  
    22.     //default element drawing
    23.     EditorGUI.PropertyField(rect, element, label, true);
    24. }
    25.  
    26. float GetElementHeight(SerializedProperty element) {
    27.  
    28.     if (DoorPro.VisualizeHinge == false) {
    29.  
    30.         switch (element.name) {
    31.  
    32.             case "HingeType":
    33.             case "HingeColor":
    34.  
    35.                 //element won't contribute to the height of the list when VisualizeHinge is false and the element is either "HingeType" or "HingeColor"
    36.                 return 0;
    37.         }
    38.     }
    39.  
    40.     //default element height
    41.     return EditorGUI.GetPropertyHeight(element, null, true);
    42. }
    I may add a "getListHeightCallback" with which you can process the entire lists height instead of per element
     
  38. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Alright, this way I can change whether or not variables are present outside of the list, based on variables inside the list, but then how would it work to make variables inside of the list 'appear' and 'dissapear' ? So I have variables inside the ListData class that shows up in the list, but then I would like to remove it based on an enum variable in that ListData class.
     
  39. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
  40. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Awesome CDF! you da man
     
  41. Aseemy

    Aseemy

    Joined:
    Aug 22, 2015
    Posts:
    207
    Hi, thanks for this wonderful plugin. Really helped me out a lot.
    I was having trouble with achieving something i wanted. To have a reorder-able list which is a property of a class. I am trying to create a tabbed UI but during design i change the order of tabs many times and then i have to change the values again an again.

    I was wondering if, in the following code, i could have "MyTabs[] tabs;" as a reorder-able list.

    Code (CSharp):
    1. public class Hi : MonoBehaviour
    2. {
    3.     public TabsManager shop, home;
    4.     void Start()
    5.     {
    6.         shop.SetButtonAction();
    7.         home.SetButtonAction();
    8.     }
    9. }
    10.  
    11. public class TabsManager
    12. {
    13.     public MyTabs[] tabs;
    14.     public int currentTab = 0;
    15.     public void ChangeTab(int index)
    16.     {
    17.         tabs[currentTab].panelButton.interactable = true;
    18.         tabs[currentTab].panelCG.alpha = 0;
    19.         tabs[currentTab].panelCG.interactable = false;
    20.         tabs[currentTab].panelCG.blocksRaycasts = false;
    21.  
    22.         tabs[index].panelCG.interactable = true;
    23.         tabs[index].panelButton.interactable = false;
    24.         tabs[index].panelCG.blocksRaycasts = true;
    25.         tabs[index].panelCG.alpha = 1;
    26.         currentTab = index;
    27.     }
    28.     public void SetButtonAction()
    29.     {
    30.         for(int i = 0;i<tabs.Length;i++)
    31.         {
    32.             tabs[i].panelButton.onClick.AddListener(() => ChangeTab(i));
    33.         }
    34.     }
    35. }
    36.  
    37. public class MyTabs
    38. {
    39.     public Button panelButton;
    40.     public CanvasGroup panelCG;
    41. }
     
    Last edited: Mar 23, 2017
  42. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Sure can. Quickest way:

    Code (CSharp):
    1. public class TabsManager
    2. {
    3.     [Reorderable]
    4.     public MyTabsArray myTabs;
    5. }
    6.  
    7. [System.Serializable]
    8. public class MyTabs
    9. {
    10.     public Button panelButton;
    11.     public CanvasGroup panelCG;
    12. }
    13.  
    14. [System.Serializable]
    15. public class MyTabsArray : ReorderableArray<MyTabs> {
    16. }
    I need to update ReorderableArray, maybe in a future update...
     
    Last edited: Mar 28, 2017
    Aseemy likes this.
  43. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    CDF, how can I draw like buttons in the list?

    I can't use
    Code (CSharp):
    1. EditorGUILayout.Toggle
    inside of the list, it doesn't show up...

    well it does show up, but outside of the list.

    So the question is basically,

    how do I display functioning buttons in a list element?
     
    Last edited: Apr 2, 2017
  44. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    You can't use GUILayout inside the list. It has already allocated space for itself and its elements.
    You need to add a callback to "drawElementCallback" and draw the using the rectangle provided.
    This rectangle will encompass the entire element and its children though, so you'll probably want to override "getElementHeightCallback" as well. Something like:

    Code (CSharp):
    1. void OnEnable() {
    2.  
    3.     list = new ReorderableList(serializedObject.FindProperty("MyList"));
    4.     list.drawElementCallback += DrawElement;
    5.     list.getElementHeightCallback += GetElementHeight;
    6. }
    7.  
    8. void DrawElement(Rect rect, SerializedProperty element, GUIContent label, bool selected, bool focused) {
    9.  
    10.     Rect propertyRect = rect;
    11.     propertyRect.yMax -= EditorGUIUtility.singleLineHeight;
    12.  
    13.     Rect toggleRect = rect;
    14.     toggleRect.yMin = propertyRect.yMax;
    15.  
    16.     EditorGUI.PropertyField(propertyRect, element, label, true);
    17.     myBool = EditorGUI.Toggle(toggleRect, myBool);
    18. }
    19.  
    20. float GetElementHeight(SerializedProperty element) {
    21.  
    22.     return EditorGUI.GetPropertyHeight(element, null, true) + EditorGUIUtility.singleLineHeight;
    23. }
     
  45. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,383
    Hmm, my coding isn't good enough to achieve that properly I think... I'm going to take a different approach here. Thanks CDF!
     
  46. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    @CDF Your nested list example does not seem to work anymore. Do you know if there is another way to do this? I'd like to have a reorderable list within another reorderable list if that is possible.
     
  47. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Hey Ben-BearFish, I've just updated the package. Check it out and let me know if you're still having issues.
    The problem here is linking a SerializedProperty to a list. It seems Unity doesn't cache SerializedProperties without an Editor controlling them, i.e a Nested list. While my solution should work, it won't be able to save the List state if you reorder elements. Not a huge deal, just means selected rows will change.
     
    Ben-BearFish likes this.
  48. tsangwailam

    tsangwailam

    Joined:
    Jul 30, 2013
    Posts:
    280
    Any chance to have it on github or bitbucket for better tracking?
     
  49. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    tsangwailam and Ben-BearFish like this.
  50. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    @CDF Thanks for the help and the example. I have it working. I had one last question for you. Is it possible to rename the words "Element" in the inspector to something else with your script with regards to the nested example?