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:
    1,313
    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,383
    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:
    1,313
    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:
    1,313
    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:
    1,313
    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:
    21
    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:
    1,313
    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:
    21
    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:
    65
    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:
    1,313
    Not sure what you mean?
     
  17. Captain_Flaush

    Captain_Flaush

    Joined:
    Apr 20, 2017
    Posts:
    65
    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:
    41
    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:
    1,313
    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:
    655
    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:
    6
    @CDF thanks for the scripts. It really saved my mental health. bless you
     
  22. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    RoyalCoder likes this.
  23. SentientSkull

    SentientSkull

    Joined:
    Apr 26, 2013
    Posts:
    75
    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. SentientSkull

    SentientSkull

    Joined:
    Apr 26, 2013
    Posts:
    75
    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:
    45
    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:
    33
    Great work on this @CDF! You've saved me a lot of headache.
     
    CDF likes this.
  27. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    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:
    1,313
    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:
    254
    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:
    1,313
    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:
    5
    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.
  32. MasonWheeler

    MasonWheeler

    Joined:
    Apr 2, 2016
    Posts:
    219
    I'm trying to get something that should be fairly simple to work, but I spent all day digging around in the bowels of this code and everything I tried failed one way or another.

    I have an abstract base type
    Item
    with several types that inherit from it. I have a type
    [Serializable]ItemList : ReorderableArray<Item> {}
    that holds multiple objects that inherit from
    Item
    . I have a custom editor that inherits from
    ReorderableDrawer
    to handle this list type.

    Here's what I need to be able to do:
    1. Make the items editable in a reorderable list
    2. Have a popup menu on the + button that selects which class to add a new instance of
    3. Make the system extensible, so new classes can be added by third parties
    Point 3 has certain implications with regards to instantiation of new items: I need to get a list of all the candidate types with Reflection, and instantiate them using some CreateInstance method and a type reference, rather than any more static, more convenient system. But no matter what I do, one of the first two points breaks.

    If I declare the base
    Item
    class with no parent type, (implicitly descending from
    System.Object
    ), it breaks the entire thing.
    ReorderablePropertyDrawer.GetList
    returns null because calling
    property.FindPropertyRelative(arrayPropertyName)
    on the
    ItemList
    property returns null because
    hasChildren
    is false for some bizarre reason which I still don't quite understand, and I have no editable list.

    If
    Item
    derives from
    UnityEngine.Object
    , it breaks point 2. I have items I can theoretically edit, but I can't create them because
    ObjectFactory.CreateInstance
    fails because... umm... reasons.

    If
    Item
    derives from
    ScriptableObject
    , point 2 works, (yay for
    ScriptableObject.CreateInstance
    !), but point 1 doesn't; I end up with a list in the editor of
    ScriptableObject
    references that wants me to drag GameObjects or Asset files onto it, rather than a list of collapsable object items whose properties I can edit.

    This should not be so difficult! I get the feeling there's one little detail I'm missing somewhere that will make everything fall into place, but for the life of me I can't figure out what it is. Any help would be appreciated.
     
  33. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    So there's a lot going on here, I'll try and break it down as easily as I can.

    If you want Item to be editable in a list but only show/serialize types that inherit from Item, you'll need to mark an array using "SerializeReference" https://docs.unity3d.com/ScriptReference/SerializeReference.html This is a new attribute Unity added in 2019.3. If you're on a version before 2019.3, Unity cannot serialize an array that contains multiple different types, if those types don't derive from UnityEngine.Object.

    from the docs: "When Unity serializes an object, it serializes all fields as value types, unless the field type derives from [UnityEngine.Object]. By default, polymorphic fields are not supported and reference based topologies, like graphs, cannot be expressed natively."

    in order for Unity to show a class value/reference in the inspector it either needs to derive from ScriptableObject or be decorated with the Serializable attribute.

    So... The solution here is to design your Item, ItemList, ItemListDrawer and derived types like this:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Test : MonoBehaviour {
    4.  
    5.     public ItemList list = new ItemList();
    6. }
    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class ItemList {
    7.  
    8.     public List<Item> Array => array;
    9.  
    10.     //Instruct Unity to Serialize the items in the array as "References" not "Values", so we can add different types of "Item"
    11.     [SerializeReference]
    12.     private List<Item> array = new List<Item>();
    13. }
    14.  
    Code (CSharp):
    1. [System.Serializable]
    2. public abstract class Item {
    3. }
    Code (CSharp):
    1. [System.Serializable]
    2. public class MyItemType1 : Item {
    3.  
    4.     public string name = "Item1";
    5.     public string stringValue = "1";
    6. }
    Code (CSharp):
    1. [System.Serializable]
    2. public class MyItemType2 : Item {
    3.  
    4.     public string name = "Item2";
    5.     public bool boolValue = true;
    6. }
    Code (CSharp):
    1. [System.Serializable]
    2. public class MyItemType3 : Item {
    3.  
    4.     public string name = "Item3";
    5.     public int intValue = 3;
    6. }
    7.  
    Code (CSharp):
    1. [System.Serializable]
    2. public class MyItemType4 : Item {
    3.  
    4.     public string name = "Item4";
    5.     public float value4 = 4;
    6. }
    7.  
    Code (CSharp):
    1. using UnityEngine;
    2. using Malee.Editor;
    3. using UnityEditor;
    4. using System;
    5.  
    6. [CustomPropertyDrawer(typeof(ItemList))]
    7. public class ItemListDrawer : ReorderableDrawer {
    8.  
    9.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    10.  
    11.         ReorderableList list = GetList(property, ARRAY_PROPERTY_NAME);
    12.  
    13.         if (list != null) {
    14.  
    15.             //register the addDropDownCallback, remove and add so we don't keep stacking callbacks
    16.  
    17.             list.onAddDropdownCallback -= OnAddItem;
    18.             list.onAddDropdownCallback += OnAddItem;
    19.             list.DoList(EditorGUI.IndentedRect(position), label);
    20.         }
    21.     }
    22.  
    23.     private void OnAddItem(Rect buttonRect, ReorderableList list) {
    24.  
    25.         //show popup menu with types derived from "Item"
    26.  
    27.         GenericMenu menu = new GenericMenu();
    28.  
    29.         var types = TypeCache.GetTypesDerivedFrom<Item>();
    30.  
    31.         foreach (var type in types) {
    32.  
    33.             menu.AddItem(new GUIContent(type.Name), false, AddItem, new AddItemData(list, type));
    34.         }
    35.  
    36.         menu.ShowAsContext();
    37.     }
    38.  
    39.     private void AddItem(object userData) {
    40.  
    41.         AddItemData data = (AddItemData)userData;
    42.  
    43.         //create instance of type and set the managed reference value on the SerializedProperty
    44.  
    45.         var value = Activator.CreateInstance(data.type);
    46.  
    47.         SerializedProperty property = data.list.AddItem();
    48.         property.managedReferenceValue = value;
    49.  
    50.         //apply changes
    51.  
    52.         property.serializedObject.ApplyModifiedProperties();
    53.     }
    54.  
    55.     private struct AddItemData {
    56.  
    57.         internal ReorderableList list;
    58.         internal Type type;
    59.  
    60.         public AddItemData(ReorderableList list, Type type) {
    61.  
    62.             this.list = list;
    63.             this.type = type;
    64.         }
    65.     }
    66. }
    Results in this:
    items.png

    I'm using TypeCache to get the types, no need for reflection: https://docs.unity3d.com/ScriptReference/TypeCache.html
    This of course assumes you're using Unity 2019.3 or newer.

    If not, then I'm afraid you'll need to derive Item from a ScriptableObject, which means to create Instances using ScriptableObject.CreateInstance(type), save that instance to the AssetDatabase using: AssetDatabase.CreateAsset(instance), then assign that reference to the objectReferenceValue of the SerializedProperty item in the array. When an object derives from UnityEngine.Object and is contained in an array, you only see the reference in the inspector, not all the fields inside that reference.

    I do however need to push an update to ReorderableList here so that managed references get proper drawn heights.

    Let me know how it goes
     
    Last edited: Apr 23, 2020
  34. MasonWheeler

    MasonWheeler

    Joined:
    Apr 2, 2016
    Posts:
    219
    Thank you soooo much! That almost works, with the exception of:

    That update would be greatly appreciated, because what I'm seeing when I expand this doesn't look like your screenshot. But the rest of it works. I'm on 2019.3, but I didn't know about
    [SerializeReference]
    . Having that makes everything fall into place. :D
     
  35. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    It's been pushed. If you're using the package, you'll need to delete the "lock" in the manifest to pull the latest.
    Unfortunately git packages don't work like Unity packages, they get cached to whatever version you installed
     
  36. MasonWheeler

    MasonWheeler

    Joined:
    Apr 2, 2016
    Posts:
    219
    Awesome! It looks great now!
     
    CDF likes this.
  37. Tetrahedrons

    Tetrahedrons

    Joined:
    May 7, 2020
    Posts:
    2
    Anyone else have this?
    Code (CSharp):
    1. A scripted class has a different serialization layout when loading.
    It popped up and I am not sure how to find the source. I made the abstract example above, and upgraded to using the package manager.

    Edit: Fixed! The problem was that prefab variants inside another prefab was not updated correctly. I guess write new scripts if you make the generic thing above, unity will not propagate nested serializables properly for some reason
     
    Last edited: May 7, 2020
  38. Tetrahedrons

    Tetrahedrons

    Joined:
    May 7, 2020
    Posts:
    2
    Hi again! Thank you so much for this nice piece. I'm getting editor lag if I have many fields when using abstract list items. It goes away if I fold the item up. It is working very well for small items, but I have a simple class with 11 int fields, and it gets really heavy. Any ideas?
     
  39. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    Do you experience the same lag when not using a ReorderableList?
    Any chance you can share a snippet of code that performs bad?
     
  40. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    @CDF
    Hey this is great work, it's very useful.

    I have an issue when creating new items in the ReorderableArray.
    I use a serialized class but upon adding a new item in the reorderably array, this instance of that class's values are not set to the default values of the class.


    To take a concrete example with the ParticlePreview class:
    "SimulationSpeed" value is set to 0 despite having a default of 1.
    And "PreviewChildren" it's set to false despite having a default of true. (See the code below)

    How can I make so that when I press the + button on the list to get a new element, the element has its values set to the default of the ParticlePreview class?

    Here's a screenshot right after pressing the "+" icon.
    upload_2020-12-7_20-24-28.png

    Code (CSharp):
    1.         [Reorderable(elementNameProperty = "Ps")]
    2.         public FxList PsPreview;

    Code (CSharp):
    1.    [Serializable]
    2.     public class FxList : ReorderableArray<ParticlePreview>
    3.     {
    4.     }
    Code (CSharp):
    1.     [Serializable]
    2.     public class ParticlePreview
    3.     {
    4.         [Tooltip("The particle system to preview. It should be a SCENE object.")]
    5.         public ParticleSystem Ps;
    6.  
    7.         [Tooltip("Delay the particle system's activation in the preview.\nDefault = 0.\n\nTIP: The ParticleSystem component has a Delay value you can modify so the preview and playmode can look the same.")]
    8.         public float Delay;
    9.  
    10.         [Tooltip("How fast the particle system simulates.\nDefault = 1.\n\nTIP: The ParticleSystem component has a Simulation Speed value you can modify so the preview and playmode can look the same.")]
    11.         public float SimulationSpeed = 1f;
    12.  
    13.         [Tooltip("Should the children particle systems also preview?")]
    14.         public bool PreviewChildren = true;
    15. }

    EDIT: Nevermind I figured it out, the answer's in the beginning of the thread.
     
    Last edited: Dec 16, 2020
  41. Toscan0

    Toscan0

    Joined:
    Oct 9, 2018
    Posts:
    11
    Hi, im using Unity 2019.4.15f1 and i can't use the package.

    I install the package by git url as the git readme says., the project manifest automatically changes to what is supposed.

    Edit: After this close unity, reopen unity, and it works.
     
    Last edited: Dec 20, 2020
  42. Jamez0r

    Jamez0r

    Joined:
    Jul 29, 2019
    Posts:
    206
    I feel like this is something simple to fix, but I am stumped. I have not made any changes to this script/configuration in the past 6 months, but sometime in the past 3 months my script with a ReorderableArray started doing this:



    My thoughts are that it has something to do with using Unity 2020.2 (or later). Unity added their own Reorderable functionality in 2020.2, and I updated to that version a couple months ago. I didn't notice that my script was having issues until now, because I haven't needed to make any changes on it for a while (but now I do, and its a crucial component).

    According to this post, Unity originally added a [Reorderable] attribute, but then opted to make lists reorderable by default, and instead added a [NonReorderable] attribute: https://forum.unity.com/threads/tha...r-can-be-reorderable-now.904778/#post-5961980

    The only thing I can think of is that this somehow this is interfering with my setup. Again, I haven't touched anything related to this configuration/script since last October, so something 'external' seems to be causing the issue. I did also add Odin Inspector to my project a couple months ago - maybe that is somehow interfering?

    This is the bit of code from ReorderableDrawer.cs that is showing the "Array must extend from ReorderableArray" message:

    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    2.  
    3.             ReorderableList list = GetList(property, attribute as ReorderableAttribute, ARRAY_PROPERTY_NAME);
    4.  
    5.             if (list != null) {
    6.  
    7.                 list.DoList(EditorGUI.IndentedRect(position), label);
    8.             }
    9.             else {
    10.  
    11.                 GUI.Label(position, "Array must extend from ReorderableArray", EditorStyles.label);
    12.             }
    13.         }
    My code is extending the ReorderableArray, and I've checked that the [Reorderable] attribute is indeed using the correct Malee one, and that the ReorderableArray class that it is extending is the correct Malee one as well.



    Would really appreciate any advice, or suggestion for anything else that I could check to try to fix this. It's blocking one of my teammates from working and we are in a crunch right now. Thanks for any help!
     
  43. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,313
    Unfortunately I cannot reproduce. I copied your code somewhat (what I could) and the list draws correctly:

    I'm using unity 2020.2.3, ReorderableList 1.0.1
    Not familiar with Odin Inspector, so maybe that's causing issues.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Malee.List;
    5.  
    6. public class SpineMixTimingSetter : MonoBehaviour {
    7.    
    8.     [System.Serializable]
    9.     public class MixList {
    10.  
    11.         [Reorderable]
    12.         public NestedChildList nested;
    13.  
    14.         [Header("Overrides")]
    15.         public bool swapToAndFromAnimationNames = false;
    16.         public float setAllMixtimesInListToThis = 0.2f;
    17.         [HideInInspector] public float previousSetAllMixtimesInListToThis = 0.2f;
    18.     }
    19.  
    20.     [System.Serializable]
    21.     public class MixTimingEntryDrawer {
    22.  
    23.         public string mixFromString;
    24.         public string mixToString;
    25.         public float mixTime = 0.2f;
    26.     }
    27.  
    28.     [System.Serializable]
    29.     public class ListOfMixLists : ReorderableArray<MixList> { }
    30.  
    31.     [System.Serializable]
    32.     public class NestedChildList : ReorderableArray<MixTimingEntryDrawer> { }
    33.  
    34.     [Header("Main Settings")]
    35.     public float defaultMix = 0.2f;
    36.  
    37.     [Space(12)]
    38.     [Reorderable] public ListOfMixLists mixLists;
    39. }
    example.png

    What happens if you just disable the [Reorderable] Attribute? What does the inspector look like?
    You probably don't even need the ReoderableList package anymore, since you're not really using its features. Since Unity 2020.2 comes with a default implementation of Reorderable Lists for all arrays now
     
  44. Jamez0r

    Jamez0r

    Joined:
    Jul 29, 2019
    Posts:
    206
    @CDF Thanks so much for the quick reply (and sorry, I wasn't expecting you/anyone to actually test out my code or else I would have copy/pasted instead of screenshotting haha).

    You are right, all I needed to do was remove the [Reorderable] attribute and it started working using Unity's reorderable arrays/lists. I think I was getting confused because I made a special "drawer" for my MixTimingEntry class, and I didn't expect it to work without using your Reorderable system. I see that my "drawer" isn't relying on that.

    Thanks again, seriously appreciate it!
     
    CDF likes this.
  45. raxar4

    raxar4

    Joined:
    Apr 25, 2019
    Posts:
    20
    Hi, i have 2 issues
    1. An expander arrow is missing in one of reorderable list.
    I don't know how to turn the arrows off or on.
    Element.png
    2. Entering a value into a property and changing the page causes the visibility of the entered values in a completely different element. After click enter, the value is confirmed in the correct element and the value in the current item displays correctly.
    How property values can be set when changing page so that it does not show up in the next item?
    propRefresh.png