{"id":1497,"date":"2016-04-30T16:45:57","date_gmt":"2016-04-30T08:45:57","guid":{"rendered":"http:\/\/www.clonefactor.com\/wordpress\/?p=1497"},"modified":"2016-04-30T17:49:27","modified_gmt":"2016-04-30T09:49:27","slug":"unity3d-reorderablelist-candy-template","status":"publish","type":"post","link":"https:\/\/www.clonefactor.com\/wordpress\/program\/c\/1497\/","title":{"rendered":"Unity3D ReorderableList candy template."},"content":{"rendered":"<p>use half day to script my favorite list handling tools.<\/p>\n<p>to support different height elements<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1498\" src=\"http:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2016\/04\/ReorderableList-candy-template.gif\" alt=\"ReorderableList candy template\" width=\"510\" height=\"323\" \/><\/p>\n<p><strong>ReorderableListExtend.cs<\/strong><\/p>\n<pre class=\"brush:csharp\">using UnityEngine;\r\nusing UnityEditor;\r\nusing UnityEditorInternal;\r\nusing System;\r\nusing System.Linq;\r\nusing System.Collections.Generic;\r\n\r\nnamespace Kit.Editor\r\n{\r\n\tpublic class ReorderableListExtend\r\n\t{\r\n\t\t#region variable\r\n\t\tSerializedObject serializedObject;\r\n\t\tSerializedProperty property;\r\n\t\tprivate string propertyName;\r\n\r\n\t\tList&lt;float&gt; elementHeights;\r\n\t\tReorderableList orderList;\r\n\r\n\t\tTexture2D backgroundImage;\r\n\t\t#endregion\r\n\r\n\t\t#region System\r\n\t\tpublic ReorderableListExtend(SerializedObject serializedObject, string propertyName,\r\n\t\t\tbool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)\r\n\t\t{\r\n\t\t\tthis.propertyName = propertyName;\r\n\t\t\tthis.serializedObject = serializedObject;\r\n\t\t\tthis.property = serializedObject.FindProperty(this.propertyName);\r\n\t\t\telementHeights = new List&lt;float&gt;(property.arraySize);\r\n\t\t\tSetHightLightBackgroundImage();\r\n\r\n\t\t\torderList = new ReorderableList(serializedObject, property, dragable, displayHeader, displayAddButton, displayRemoveButton);\r\n\t\t\torderList.onAddCallback += OnAdd;\r\n\t\t\torderList.onSelectCallback += OnSelect;\r\n\t\t\torderList.onRemoveCallback += OnRemove;\r\n\t\t\torderList.drawHeaderCallback += OnDrawHeader;\r\n\t\t\torderList.drawElementCallback += OnDrawElement;\r\n\t\t\torderList.drawElementBackgroundCallback += OnDrawElementBackground;\r\n\t\t\torderList.elementHeightCallback += OnCalculateItemHeight;\r\n\t\t}\r\n\r\n\t\t~ReorderableListExtend()\r\n\t\t{\r\n\t\t\torderList.onAddCallback -= OnAdd;\r\n\t\t\torderList.onSelectCallback -= OnSelect;\r\n\t\t\torderList.onRemoveCallback -= OnRemove;\r\n\t\t\torderList.drawHeaderCallback -= OnDrawHeader;\r\n\t\t\torderList.drawElementCallback -= OnDrawElement;\r\n\t\t\torderList.drawElementBackgroundCallback -= OnDrawElementBackground;\r\n\t\t\torderList.elementHeightCallback -= OnCalculateItemHeight;\r\n\t\t\tbackgroundImage = null;\r\n\t\t}\r\n\t\t#endregion\r\n\r\n\t\t#region API\r\n\t\tpublic virtual void SetHightLightBackgroundImage()\r\n\t\t{\r\n\t\t\tbackgroundImage = new Texture2D(3, 1);\r\n\t\t\tbackgroundImage.SetPixel(0, 0, new Color(0f, .8f, .7f));\r\n\t\t\tbackgroundImage.hideFlags = HideFlags.DontSave;\r\n\t\t\tbackgroundImage.wrapMode = TextureWrapMode.Clamp;\r\n\t\t\tbackgroundImage.Apply();\r\n\t\t}\r\n\r\n\t\tpublic void DoLayoutList()\r\n\t\t{\r\n\t\t\torderList.DoLayoutList();\r\n\t\t}\r\n\r\n\t\tpublic void DoList(Rect rect)\r\n\t\t{\r\n\t\t\torderList.DoList(rect);\r\n\t\t}\r\n\t\t#endregion\r\n\r\n\t\t#region listener\r\n\t\tprotected virtual void OnDrawHeader(Rect rect)\r\n\t\t{\r\n\t\t\tEditorGUI.LabelField(rect, property.displayName);\r\n\t\t}\r\n\t\t\r\n\t\tprivate void OnAdd(ReorderableList list)\r\n\t\t{\r\n\t\t\tint index = list.serializedProperty.arraySize;\r\n\t\t\tlist.serializedProperty.arraySize++;\r\n\t\t\tlist.index = index;\r\n\t\t\tSerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);\r\n\t\t\tOnAdd(list, element);\r\n\t\t}\r\n\r\n\t\tprivate void OnRemove(ReorderableList list)\r\n\t\t{\r\n\t\t\tSerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);\r\n\t\t\tOnRemove(list, element);\r\n\t\t}\r\n\r\n\t\tprivate void OnSelect(ReorderableList list)\r\n\t\t{\r\n\t\t\tSerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);\r\n\t\t\tOnSelect(list, element);\r\n\t\t}\r\n\r\n\t\tprivate void OnDrawElement(Rect rect, int index, bool active, bool focused)\r\n\t\t{\r\n\t\t\tif (property == null || property.arraySize &lt;= index)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tSerializedProperty element = property.GetArrayElementAtIndex(index);\r\n\r\n\t\t\tfloat height = EditorGUI.GetPropertyHeight(element) + EditorGUIUtility.standardVerticalSpacing;\r\n\t\t\tRenewElementHeight(index, height);\r\n\t\t\trect.height = height;\r\n\t\t\trect.width -= 40;\r\n\t\t\trect.x += 20;\r\n\t\t\t\r\n\t\t\tOnDrawElement(rect, index, active, focused, element);\r\n\t\t}\r\n\t\tprivate void OnDrawElementBackground(Rect rect, int index, bool active, bool focused)\r\n\t\t{\r\n\t\t\tif (property == null || property.arraySize &lt;= index)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tSerializedProperty element = property.GetArrayElementAtIndex(index);\r\n\t\t\tfloat height = elementHeights[index];\r\n\t\t\trect.height = height;\r\n\t\t\trect.width -= 4;\r\n\t\t\trect.x += 2;\r\n\r\n\t\t\tOnDrawElementBackground(rect, index, active, focused, element, height);\r\n\t\t}\r\n\t\t#endregion\r\n\r\n\t\t#region Template\r\n\t\tprotected virtual void OnAdd(ReorderableList list, SerializedProperty newElement) { }\r\n\t\tprotected virtual void OnSelect(ReorderableList list, SerializedProperty selectedElement) { }\r\n\t\tprotected virtual void OnRemove(ReorderableList list, SerializedProperty deleteElement)\r\n\t\t{\r\n\t\t\tif (EditorUtility.DisplayDialog(\r\n\t\t\t\t\"Warning !\",\r\n\t\t\t\t\"Are you sure you want to delete:\\n\\r[ \" + deleteElement.displayName + \" ] ?\",\r\n\t\t\t\t\"Yes\", \"No\"))\r\n\t\t\t{\r\n\t\t\t\tReorderableList.defaultBehaviours.DoRemoveButton(list);\r\n\t\t\t}\r\n\t\t}\r\n\t\tprotected virtual void OnDrawElement(Rect rect, int index, bool active, bool focused, SerializedProperty element)\r\n\t\t{\r\n\t\t\tEditorGUI.PropertyField(rect, element, true);\r\n\t\t}\r\n\t\tprotected virtual void OnDrawElementBackground(Rect rect, int index, bool active, bool focused, SerializedProperty element, float height)\r\n\t\t{\r\n\t\t\tif(active)\r\n\t\t\t\tEditorGUI.DrawTextureTransparent(rect, backgroundImage, ScaleMode.ScaleAndCrop);\r\n\t\t}\r\n\t\t#endregion\r\n\r\n\t\t#region height hotfix\r\n\t\tprivate void RenewElementHeight(int index, float height)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\telementHeights[index] = height;\r\n\t\t\t}\r\n\t\t\tcatch\r\n\t\t\t{\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tElementListOverflowFix();\r\n\t\t\t}\r\n\t\t}\r\n\t\tprivate float OnCalculateItemHeight(int index)\r\n\t\t{\r\n\t\t\tfloat height = 0f;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif(height != elementHeights[index])\r\n\t\t\t\t{\r\n\t\t\t\t\theight = elementHeights[index];\r\n\t\t\t\t\tEditorUtility.SetDirty(serializedObject.targetObject);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch\r\n\t\t\t{\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tElementListOverflowFix();\r\n\t\t\t}\r\n\t\t\treturn height;\r\n\t\t}\r\n\t\tprivate void ElementListOverflowFix()\r\n\t\t{\r\n\t\t\tif (property.arraySize != elementHeights.Count)\r\n\t\t\t{\r\n\t\t\t\tfloat[] floats = elementHeights.ToArray();\r\n\t\t\t\tArray.Resize(ref floats, property.arraySize);\r\n\t\t\t\telementHeights = floats.ToList();\r\n\t\t\t\tEditorUtility.SetDirty(serializedObject.targetObject);\r\n\t\t\t}\r\n\t\t}\r\n\t\t#endregion\r\n\t}\r\n}<\/pre>\n<h1>How to use it ?<\/h1>\n<p>use it without override.<\/p>\n<pre class=\"brush:csharp\">using UnityEngine;\r\nusing UnityEditor;\r\n\r\nusing Kit.Editor;\r\n\r\nnamespace Kit.UI\r\n{\r\n\t[CustomEditor(typeof(PanelQueue))]\r\n\tpublic class PanelQueueEditor : UnityEditor.Editor\r\n\t{\r\n\t\treadonly GUIContent prefabListLabel = new GUIContent(\"Prefab Queue\", \"Queue for loaded\");\r\n\t\tReorderableListExtend prefabList;\r\n\r\n\t\tprivate void OnEnable()\r\n\t\t{\r\n\t\t\t\/\/ Step 1, init\r\n\t\t\tprefabList = new ReorderableListExtend(serializedObject, \"m_PrefabBehaviour\");\r\n\t\t}\r\n\r\n\t\tpublic override void OnInspectorGUI()\r\n\t\t{\r\n\t\t\tserializedObject.Update();\r\n\r\n\t\t\tEditorGUILayout.PropertyField(serializedObject.FindProperty(\"m_Canvas\"));\r\n\r\n\t\t\t\/\/ Step 2, print it out.\r\n\t\t\tprefabList.DoLayoutList();\r\n\r\n\t\t\tif (GUI.changed)\r\n\t\t\t\tserializedObject.ApplyModifiedProperties();\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Use it as template and override with your own method.<\/p>\n<pre class=\"brush:csharp\">\/\/ Step 1: inheriting &lt;ReorderableListExtend&gt;\r\npublic class MyList : ReorderableListExtend\r\n{\r\n\t\/\/ Step 2: redirect constructor\r\n\tpublic MyList(SerializedObject serializedObject, string propertyName,\r\n\t\tbool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)\r\n\t\t: base(serializedObject, propertyName, dragable, displayHeader, displayAddButton, displayRemoveButton)\r\n\t{}\r\n\r\n\t\/\/ Step 3: using override to re-wrote function(s)\r\n\t\/\/\r\n\t\/\/\r\n}<\/pre>\n<p>hints: use IDE to do that. make your life easier<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1503\" src=\"http:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2016\/04\/Capture.jpg\" alt=\"IDE make your life easier\" width=\"879\" height=\"384\" srcset=\"https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2016\/04\/Capture.jpg 879w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2016\/04\/Capture-300x131.jpg 300w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2016\/04\/Capture-768x336.jpg 768w\" sizes=\"auto, (max-width: 879px) 100vw, 879px\" \/><\/p>\n<p>that&#8217;s all we need.<\/p>\n<h2>know bugs:<\/h2>\n<ul>\n<li>during reorder the element&#8217;s child information might not follow the existing collapse status.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>use half day to script my favorite list handling t &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,11],"tags":[20,43],"class_list":["post-1497","post","type-post","status-publish","format-standard","hentry","category-c","category-unity3d","tag-editor","tag-unity3d-2"],"_links":{"self":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1497"}],"version-history":[{"count":0,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1497\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}