Search Unity

Reorderable List v2

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

  1. YaserDev

    YaserDev

    Joined:
    Aug 17, 2016
    Posts:
    20
    Hello.
    I have added the package to my project, but I can't use the directory Malee, it says it's not found.
    I have tried adding a reference to it in visual studio, by right-clicking the project assembly, then AddReferernce, but I get an error when I try to add the Rotorz package that says it can't be added, without any other message.
    Has anyone faced such a problem?
    Thanks
    (Nevermind: Turns out Malee is not the directory of the package itself, but it was in the example on git hub, I used it before but accidentally deleted the folder)
     
    Last edited: Mar 16, 2018
  2. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    Hey just a small update here, I've added some new features to the ReorderableList:

    pagination - ability to separate the list into pages, helpful if you're dealing with massive lists
    sorting - ability to sort list based on a field, or the value of each element
    custom label per element - "getElementLabelCallback"

    Let me know what you think, and any bugs you might find :)
     
    alexanderameye likes this.
  3. ferdielance

    ferdielance

    Joined:
    May 31, 2018
    Posts:
    1
    I just discovered this library and it's working perfectly. I was trying to display a list of objects with various custom drawers, and nothing else worked smoothly; this made my life much easier. Thanks a lot!
     
  4. alexanderameye

    alexanderameye

    Joined:
    Nov 27, 2013
    Posts:
    1,048
    Hey, is there a way to know if a certain list element was selected and return it as a Boolean in code?

    Thanks in advance,

    Alex
     
  5. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    You can get selection indices and loop over:

    Code (CSharp):
    1. var selected = list.Selected;
    2.  
    3. foreach (int index in selected) {
    4.  
    5.     var element = list.GetItem(index);
    6. }
     
    alexanderameye likes this.
  6. TylerO

    TylerO

    Joined:
    Aug 21, 2011
    Posts:
    35
    To add to the following answer above, when you get an item by Index, how do you cast it back to it's original object? I have tried to do the following, but it doesn't seem to work:

    Code (CSharp):
    1.  
    2. foreach (int index in selectedIndexes)
    3. {
    4.     selectedProperty = itemList.GetItem(index);
    5.     selectedItem = (ItemDefinition)selectedProperty.objectReferenceValue;
    6. }
    7.  

    Great plugin, just need to figure out the above before I can move forward haha
     
  7. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    your list/array is of ItemDefinition type?
    Is ItemDefinition an actual unity object?
    what errors are you getting?
     
  8. TylerO

    TylerO

    Joined:
    Aug 21, 2011
    Posts:
    35
    Hi @CDF, thanks for the quick reply!

    Following the examples, I wrote the following:

    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public class ItemDefinition : UnityEngine.Object
    4.     {
    5.         /// <summary>
    6.         /// The unique of ID of this item definition
    7.         /// </summary>
    8.         public int id;
    9.         public string itemName;
    10.  
    11.         public bool canCraft;
    12.         public bool canGenerate;
    13.  
    14.         //  Item Appearance
    15.         //      Position
    16.         //      Rotation
    17.         //      Scale
    18.         //      Models variation based on Race/Gender
    19.         public List<ItemObject> objects;
    20.  
    21.         //  Item Recipe
    22.         public ItemRecipe recipe;
    23.  
    24.         //  Item Set
    25.         public ItemSet set;
    26.     }
    27.  
    28.     [Serializable]
    29.     public class ItemDefinitionList : ReorderableArray<ItemDefinition> {  }
    30.  
    ItemDefinition does indeed inherit UnityEngine.Object and the actual ReorderableList is using the ItemDefinitionList. It was my understanding that the 'GetItem' method would return an item from the ReorderableArray of ItemDefinitionList, which, is an ItemDefintion.

    If my understanding is incorrect, please let me know - as for the error, `selectedItem` is null. If you have any other questions, please do not hesitate to ask - and I appreciate the help very much!

    Edit:

    If it helps, here is also the ItemDatabase class (which contains the ItemDefinitionList with the 'Reorderable' attribute)


    Code (CSharp):
    1.  
    2. public class ItemDatabase : ScriptableObject
    3. {
    4.     [SerializeField, Reorderable]
    5.     public ItemDefinitionList itemDefinitionList;
    6. }
    7.  
    As well as how the ReorderableList is setup:

    Code (CSharp):
    1.  
    2. // Do we have an existing ItemDatabase?
    3. itemDatabase = Resources.Load<ItemDatabase>("Assets/Databases/ItemDatabase.asset");
    4. if (itemDatabase == null)
    5. {
    6.     // Create a new ItemDatabase
    7.     itemDatabase = (ItemDatabase)ScriptableObject.CreateInstance("ItemDatabase");
    8.     // Save it
    9.     AssetDatabase.CreateAsset(itemDatabase, "Assets/Databases/ItemDatabase.asset");
    10.     AssetDatabase.SaveAssets();
    11. }
    12. // Create our Item Definition List
    13. itemDbSerialized = new SerializedObject(itemDatabase);
    14. itemList = new ReorderableList(itemDbSerialized.FindProperty("itemDefinitionList"), true, true, false, ReorderableList.ElementDisplayType.Auto, "Item Definitions", null);
    15.  
     
    Last edited: Dec 18, 2018
  9. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    The issue is that ItemDefinition extends UnityEngine.Object
    Unity doesn't create UnityEngine.Objects automatically for you, so the reference will always be null unless you assign an object to it.

    So how to do assign an object to an entry in the array?

    extend from ScriptableObject. And use the "CreateAssetMenu" attribute:

    Code (CSharp):
    1. [CreateAssetMenu]
    2. public class ItemDefinition : ScriptableObject {
    Now you can create an "ItemDefinition" object in your project, then drag and drop that asset into the list inspector.

    If your intention was to simply use a default C# object. Then just define the class like so:

    Code (CSharp):
    1. [Serializable]
    2. public class ItemDefinition {
    Now, because ItemDefinition is just a class and is Serializable, the Unity serializer can automatically generate and serialize your object.

    Hope that makes sense.
    Try changing this:

    Code (CSharp):
    1. [SerializeField, Reorderable]
    2. public ItemDefinitionList itemDefinitionList;
    to

    Code (CSharp):
    1. [SerializeField]
    2. public ItemDefinition[] itemDefinitionList;
    And see how Unity renders the inspector window. You'll notice an array accepting Object references, and as above, you're responsible for creating and assigning the references when a class inherits from UnityEngine.Object.

    *EDIT*
    I realize the documentation is lacking, I'm just too busy with work to actually do anything about. It is something I'm always thinking about though ;)
     
  10. TylerO

    TylerO

    Joined:
    Aug 21, 2011
    Posts:
    35
    It might be worth mentioning that I am actually working with an Editor Window, not an inspector.

    Ideally, I would like to keep it to where adding an ItemDefinition is as simple as pressing the 'add' button in the ReorderableList, as opposed to creating an instance of a ScriptableObject and assigning it (although, I suppose I could override what the add button does) so having ItemDefinition as just a class would be ideal.

    And I think I understand what you're getting at - I don't actually need an ItemDefinitionList class (which I explicitly created for the ReorderableArray) and instead, I should just be able to created a ReorderableList from the array (or list?) of ItemDefinitions - am I correct in that thinking?

    I'll take the above and work on making changes to my code. Again, thanks for the help :)
     
  11. Totoro83y

    Totoro83y

    Joined:
    Sep 21, 2013
    Posts:
    18
    Hi, first of all I want to say thanks for this great plugin.

    Next, I'm having a very strange bug (or at least it seems so), and I'm not sure if it is about your software or unity itself.

    If I don't add any function in the event drawElementCallback all works as espected.
    Instead, if I insert a very simple function that mimics the plugin normal behaviour, like this:
    Code (CSharp):
    1.     private void Element(Rect rect, SerializedProperty element, GUIContent label, bool selected, bool focused)
    2.     {
    3.         EditorGUI.PropertyField(rect, element, label, true);
    4.     }
    it works but all the editing elements in the TextFields are not showed. I don't see any highlight in text, any caret, anything at all. The TextFields works as expected even if they are not showed, I can edit anything, select anything, just I don't see them...
     
    Last edited: Apr 24, 2019
  12. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    Hmm, I can't reproduce it. There must be something else capturing focus if you're not seeing a caret.
    Have you tried doing this in an empty project?

    For reference, here's the code inside ReorderableList that handles drawing elements:
    Code (CSharp):
    1.  
    2. if (drawElementCallback != null) {
    3.  
    4.     drawElementCallback(renderRect, element, label, selected, focused);
    5. }
    6. else {
    7.  
    8.     EditorGUI.PropertyField(renderRect, element, label, true);
    9. }
    10.  
    So I can't see why implementing drawElementCallback and doing the exact same thing wouldn't work!
     
  13. Totoro83y

    Totoro83y

    Joined:
    Sep 21, 2013
    Posts:
    18
    I don't think the problem is just the focus captured elsewhere because I can't even see the highlights in text, if I try to select it.
    It is a very strange problem, and I had it in another piece of code, too, so I'm starting to think that it is a bug of the current unity version (2019.1.0f2). However I didn't try with a very simple and minimal project so I'm not sure.
     
  14. studiostartunity

    studiostartunity

    Joined:
    May 3, 2019
    Posts:
    24
    This components is fantastic but it has a lot of rendering problems when used heavily. For example, when I click a text field it suddenly moves in another position, or when scrolling up and down the Inspector panel, the rendering stopped working for a second and the list appears with it's original Unity graphics, and so on. I'm experiencing this on 2019 versions, so I can suppose it's a rendering problem of those last versions.
     
  15. Captain_Flaush

    Captain_Flaush

    Joined:
    Apr 20, 2017
    Posts:
    42
    Hey,

    Is there any way to make a nested list without having an extra element?

    Extra Elem.PNG

    How should I approach this ?

    Thanks!
     
  16. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    Not sure what you mean?
     
  17. Captain_Flaush

    Captain_Flaush

    Joined:
    Apr 20, 2017
    Posts:
    42
    I mean, I want to have a reordable list with each element a reordable list.
    Right now there is a reordable list with each element a class that has a member a reordable list.
     
  18. btsn

    btsn

    Joined:
    May 12, 2017
    Posts:
    2
    Is there a way to do things to objects that are being added to a nested list?

    Code (CSharp):
    1. public class AnimationData : ScriptableObject {
    2.     [SerializeField, Reorderable(surrogateType = typeof(Sprite), surrogateProperty = "objectProperty") ]
    3.     public FrameList[] animation;
    4. }
    5.  
    6. [Serializable]
    7. public class FrameList : ReorderableArray<FrameData> { }
    8. [Serializable]
    9. public class FrameData {
    10.     public Sprite sprite;
    11.     public float duration;
    12. }
    I have this array of arrays. I would like to be able to drag multiple sprites into the list and have it add them in order. If I drag them, it creates the proper amount of items, but the items are empty. I believe that I would also need to cast the item being dragged into the inspector to a sprite, as by default it would be a texture2D.
     
  19. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    I believe "surrogateProperty" should be "sprite" in this case.
    As for additional surrogate functionality, you would need to write a custom editor to handle.
    Like this example: https://github.com/cfoulston/Unity-.../master/Example/Editor/SurrogateTestEditor.cs
     
  20. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    616
    Hi
    Am porting from Rotorz reorderable list and need a missing feature. I was just about to start implementing it myself when I thought to check (1) whether there's already a way to do it and if not (2) if my changes would be received back to the repo?

    EDIT: Just found `onAddDropdownCallback` ! Nevermind! Please ignore the rest of this :D

    My goal: I wish to present a choice of options when the Add button is pressed and only create a new slot with that choice when one is clicked.

    As you might guess, I'm serializing a ScriptableObject subclass (A) that holds (in the same asset) instances of subclasses of ScriptableObject subclass (B) (i.e. (C), (D) and (E) all extend (B) and they're stored in an array on (A)).

    If (2) I'll create an optional delegate for the add button which, if set, overrides the default add behaviour. That callback can then deal with creating the popup (GenericMenu) in the correct place (supplied via the callback) and deal with updating the array etc.

    Thx & HTH
     
    Last edited: Nov 27, 2019
  21. Argenuto

    Argenuto

    Joined:
    Apr 7, 2014
    Posts:
    2
    @CDF thanks for the scripts. It really saved my mental health. bless you
     
  22. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    InfinityCoder88 likes this.
  23. kphipps

    kphipps

    Joined:
    Apr 26, 2013
    Posts:
    59
    Could someone show an example of how to set the element text from a specific field? In this case I want to have the label for the element coming from an enum field. I tried making a custom editor script and using 'getElementLabelCallback' but I'm not sure how its supposed to work. I'm also not sure how to access the enum field for each element to get the text for the label.

    Code (CSharp):
    1. public class ReorderableListTest : MonoBehaviour
    2. {
    3.     [System.Serializable]
    4.     public struct AnimRec
    5.     {
    6.         public string name;
    7.         public AnimationAction animAction;
    8.         public AnimationClip[] clips;
    9.     }
    10.  
    11.     [System.Serializable]
    12.     public class AnimRecList : ReorderableArray<AnimRec>{}
    13.  
    14.     [Reorderable]
    15.     public AnimRecList animationClips;
    16. }
    Code (CSharp):
    1. [CustomEditor(typeof(ReorderableListTest))]
    2. public class ReorderableListTestEditor : Editor
    3. {
    4.     private ReorderableList list;
    5.    
    6.     void OnEnable()
    7.     {
    8.         list = new ReorderableList(serializedObject.FindProperty("animationClips"));
    9.         list.getElementLabelCallback += GetElementLabelCallback;
    10.     }
    11.  
    12.     private GUIContent GetElementLabelCallback(SerializedProperty element)
    13.     {
    14.         var guiContent = new GUIContent();
    15.  
    16.         guiContent.text = "Label Test";
    17.  
    18.         return guiContent;
    19.     }
    20.  
    21.     public override void OnInspectorGUI()
    22.     {
    23.         //Update the fields so they show current data
    24.         serializedObject.Update();
    25.  
    26.         list.DoLayoutList();
    27.  
    28.         //Apply changes made in the inspector to the fields.
    29.         serializedObject.ApplyModifiedProperties();
    30.     }
    31. }
     
  24. kphipps

    kphipps

    Joined:
    Apr 26, 2013
    Posts:
    59
    Ok, I think I got it working. I had been mixing the different methods to set the element name I guess and some other things I did incorrectly.
     
  25. asger60

    asger60

    Joined:
    Mar 27, 2013
    Posts:
    20
    this is maybe more of a Unity bug, but I find that recompiling (any) editor code while I have a scriptable object (with the list code) selected - will make the list code break with a null ref. The only solution I have found so far, is to restart Unity.
     
  26. LandePlage

    LandePlage

    Joined:
    May 21, 2016
    Posts:
    2
    Great work on this @CDF! You've saved me a lot of headache.
     
    CDF likes this.
  27. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    Can you share a video or image? I'm unable to reproduce this.
    Is there any additional information you can share about the null ref error? What's the stack trace look like?
     
  28. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    Looks like Unity 2019.3 changed some styling on me. Little busy with other work now, but hopefully soon I can fix some of the layout issues
     
  29. Reahreic

    Reahreic

    Joined:
    Mar 23, 2011
    Posts:
    115
    I'm not sure it's specific to a ScriptableObject as i get a similar error when saving my scripts and i'm just using a basic list of a data class.

    1st error:
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. UnityEditor.EditorStyles.get_miniTextField () (at C:/buildslave/unity/build/Editor/Mono/GUI/EditorStyles.cs:96)
    3. Malee.Editor.ReorderableList+Style..cctor () (at Assets/Scripts/Reorderable Lists/Editor/ReorderableList.cs:1865)
    4. Rethrow as TypeInitializationException: The type initializer for 'Style' threw an exception.
    5. Malee.Editor.ReorderableList..ctor (UnityEditor.SerializedProperty list, System.Boolean canAdd, System.Boolean canRemove, System.Boolean draggable, Malee.Editor.ReorderableList+ElementDisplayType elementDisplayType, System.String elementNameProperty, System.String elementNameOverride, UnityEngine.Texture elementIcon) (at Assets/Scripts/Reorderable Lists/Editor/ReorderableList.cs:185)
    6. Malee.Editor.ReorderableList..ctor (UnityEditor.SerializedProperty list, System.Boolean canAdd, System.Boolean canRemove, System.Boolean draggable) (at Assets/Scripts/Reorderable Lists/Editor/ReorderableList.cs:135)
    7. Malee.Editor.ReorderableList..ctor (UnityEditor.SerializedProperty list) (at Assets/Scripts/Reorderable Lists/Editor/ReorderableList.cs:131)
    8. ClassEditor.OnEnable () (at Assets/Scenes/Reorderable Lists/Editor/ClassEditor.cs:13)
    9.  
    thereafter these spam the console anytime the mouse moves.
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. ClassEditor.OnInspectorGUI () (at Assets/Scenes/Reorderable Lists/Editor/ClassEditor.cs:21)
    3. UnityEditor.UIElements.InspectorElement+<CreateIMGUIInspectorFromEditor>c__AnonStorey1.<>m__0 () (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorElement.cs:501)
    4. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at C:/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:179)
    5.  
    Code that trips it up (Unity 2019.2.11f1):
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System;
    5.  
    6. public class UseExample : MonoBehaviour {
    7.     public List<MyData> myData = new List<MyData>();
    8. }
    9.  
    10. [Serializable]
    11. public class MyData {
    12.     public string name = "";
    13.     public enum Visibility {
    14.         Hide,
    15.         Show
    16.     }
    17.  
    18.     public Visibility visibility = Visibility.Hide;
    19.  
    20.     public List<SubData> lstSubData = new List<SubData>();
    21. }
    22.  
    23. [Serializable]
    24. public class SubData {
    25.     public string name = "";
    26.     public bool isAlive = false;
    27. }
    28.  
    29. /*ClassEditor.cs*/
    30. using UnityEngine;
    31. using UnityEditor;
    32. using System.Collections;
    33. using Malee.Editor;
    34. using System;
    35.  
    36. [CanEditMultipleObjects]
    37. [CustomEditor(typeof(UseExample))]
    38. public class ClassEditor : Editor {
    39.     private ReorderableList myData;
    40.  
    41.     void OnEnable() {
    42.         myData = new ReorderableList(serializedObject.FindProperty("myData"));
    43.         myData.elementNameProperty = "name";
    44.     }
    45.  
    46.     public override void OnInspectorGUI() {
    47.         serializedObject.Update();
    48.  
    49.         //draw the list using GUILayout, you can of course specify your own position and label
    50.         myData.DoLayoutList();
    51.  
    52.         serializedObject.ApplyModifiedProperties();
    53.     }
    54. }
    55.  
     
  30. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    799
    This should be fixed now.

    Reason was GUI skin is not loaded OnEnable and I was accessing Style information to determine header and footer heights.

    Fixed by just hard-coding the values :)
     
  31. Artifact-Jesse

    Artifact-Jesse

    Joined:
    May 5, 2013
    Posts:
    6
    Just wanted to drop in and say that this is a godsend. I cannot thank you enough for writing this and doing the extra work to make it easily available as a package, to boot. ♥
     
    CDF likes this.
unityunity