Unity3D ReorderableList candy template.

use half day to script my favorite list handling tools.

to support different height elements

ReorderableList candy template

ReorderableListExtend.cs

using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System;
using System.Linq;
using System.Collections.Generic;

namespace Kit.Editor
{
	public class ReorderableListExtend
	{
		#region variable
		SerializedObject serializedObject;
		SerializedProperty property;
		private string propertyName;

		List<float> elementHeights;
		ReorderableList orderList;

		Texture2D backgroundImage;
		#endregion

		#region System
		public ReorderableListExtend(SerializedObject serializedObject, string propertyName,
			bool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)
		{
			this.propertyName = propertyName;
			this.serializedObject = serializedObject;
			this.property = serializedObject.FindProperty(this.propertyName);
			elementHeights = new List<float>(property.arraySize);
			SetHightLightBackgroundImage();

			orderList = new ReorderableList(serializedObject, property, dragable, displayHeader, displayAddButton, displayRemoveButton);
			orderList.onAddCallback += OnAdd;
			orderList.onSelectCallback += OnSelect;
			orderList.onRemoveCallback += OnRemove;
			orderList.drawHeaderCallback += OnDrawHeader;
			orderList.drawElementCallback += OnDrawElement;
			orderList.drawElementBackgroundCallback += OnDrawElementBackground;
			orderList.elementHeightCallback += OnCalculateItemHeight;
		}

		~ReorderableListExtend()
		{
			orderList.onAddCallback -= OnAdd;
			orderList.onSelectCallback -= OnSelect;
			orderList.onRemoveCallback -= OnRemove;
			orderList.drawHeaderCallback -= OnDrawHeader;
			orderList.drawElementCallback -= OnDrawElement;
			orderList.drawElementBackgroundCallback -= OnDrawElementBackground;
			orderList.elementHeightCallback -= OnCalculateItemHeight;
			backgroundImage = null;
		}
		#endregion

		#region API
		public virtual void SetHightLightBackgroundImage()
		{
			backgroundImage = new Texture2D(3, 1);
			backgroundImage.SetPixel(0, 0, new Color(0f, .8f, .7f));
			backgroundImage.hideFlags = HideFlags.DontSave;
			backgroundImage.wrapMode = TextureWrapMode.Clamp;
			backgroundImage.Apply();
		}

		public void DoLayoutList()
		{
			orderList.DoLayoutList();
		}

		public void DoList(Rect rect)
		{
			orderList.DoList(rect);
		}
		#endregion

		#region listener
		protected virtual void OnDrawHeader(Rect rect)
		{
			EditorGUI.LabelField(rect, property.displayName);
		}
		
		private void OnAdd(ReorderableList list)
		{
			int index = list.serializedProperty.arraySize;
			list.serializedProperty.arraySize++;
			list.index = index;
			SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
			OnAdd(list, element);
		}

		private void OnRemove(ReorderableList list)
		{
			SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);
			OnRemove(list, element);
		}

		private void OnSelect(ReorderableList list)
		{
			SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);
			OnSelect(list, element);
		}

		private void OnDrawElement(Rect rect, int index, bool active, bool focused)
		{
			if (property == null || property.arraySize <= index)
				return;

			SerializedProperty element = property.GetArrayElementAtIndex(index);

			float height = EditorGUI.GetPropertyHeight(element) + EditorGUIUtility.standardVerticalSpacing;
			RenewElementHeight(index, height);
			rect.height = height;
			rect.width -= 40;
			rect.x += 20;
			
			OnDrawElement(rect, index, active, focused, element);
		}
		private void OnDrawElementBackground(Rect rect, int index, bool active, bool focused)
		{
			if (property == null || property.arraySize <= index)
				return;

			SerializedProperty element = property.GetArrayElementAtIndex(index);
			float height = elementHeights[index];
			rect.height = height;
			rect.width -= 4;
			rect.x += 2;

			OnDrawElementBackground(rect, index, active, focused, element, height);
		}
		#endregion

		#region Template
		protected virtual void OnAdd(ReorderableList list, SerializedProperty newElement) { }
		protected virtual void OnSelect(ReorderableList list, SerializedProperty selectedElement) { }
		protected virtual void OnRemove(ReorderableList list, SerializedProperty deleteElement)
		{
			if (EditorUtility.DisplayDialog(
				"Warning !",
				"Are you sure you want to delete:\n\r[ " + deleteElement.displayName + " ] ?",
				"Yes", "No"))
			{
				ReorderableList.defaultBehaviours.DoRemoveButton(list);
			}
		}
		protected virtual void OnDrawElement(Rect rect, int index, bool active, bool focused, SerializedProperty element)
		{
			EditorGUI.PropertyField(rect, element, true);
		}
		protected virtual void OnDrawElementBackground(Rect rect, int index, bool active, bool focused, SerializedProperty element, float height)
		{
			if(active)
				EditorGUI.DrawTextureTransparent(rect, backgroundImage, ScaleMode.ScaleAndCrop);
		}
		#endregion

		#region height hotfix
		private void RenewElementHeight(int index, float height)
		{
			try
			{
				elementHeights[index] = height;
			}
			catch
			{
			}
			finally
			{
				ElementListOverflowFix();
			}
		}
		private float OnCalculateItemHeight(int index)
		{
			float height = 0f;
			try
			{
				if(height != elementHeights[index])
				{
					height = elementHeights[index];
					EditorUtility.SetDirty(serializedObject.targetObject);
				}
			}
			catch
			{
			}
			finally
			{
				ElementListOverflowFix();
			}
			return height;
		}
		private void ElementListOverflowFix()
		{
			if (property.arraySize != elementHeights.Count)
			{
				float[] floats = elementHeights.ToArray();
				Array.Resize(ref floats, property.arraySize);
				elementHeights = floats.ToList();
				EditorUtility.SetDirty(serializedObject.targetObject);
			}
		}
		#endregion
	}
}

How to use it ?

use it without override.

using UnityEngine;
using UnityEditor;

using Kit.Editor;

namespace Kit.UI
{
	[CustomEditor(typeof(PanelQueue))]
	public class PanelQueueEditor : UnityEditor.Editor
	{
		readonly GUIContent prefabListLabel = new GUIContent("Prefab Queue", "Queue for loaded");
		ReorderableListExtend prefabList;

		private void OnEnable()
		{
			// Step 1, init
			prefabList = new ReorderableListExtend(serializedObject, "m_PrefabBehaviour");
		}

		public override void OnInspectorGUI()
		{
			serializedObject.Update();

			EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Canvas"));

			// Step 2, print it out.
			prefabList.DoLayoutList();

			if (GUI.changed)
				serializedObject.ApplyModifiedProperties();
		}
	}
}

Use it as template and override with your own method.

// Step 1: inheriting <ReorderableListExtend>
public class MyList : ReorderableListExtend
{
	// Step 2: redirect constructor
	public MyList(SerializedObject serializedObject, string propertyName,
		bool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)
		: base(serializedObject, propertyName, dragable, displayHeader, displayAddButton, displayRemoveButton)
	{}

	// Step 3: using override to re-wrote function(s)
	//
	//
}

hints: use IDE to do that. make your life easier

IDE make your life easier

that’s all we need.

know bugs:

  • during reorder the element’s child information might not follow the existing collapse status.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料