Initial Checkin of new Re-Orderable List.

Still ToDo:
1: complete testing scenarios
2: Update Editor script to add new auto-create option

--HG--
branch : develop_5.2
pull/413/head
Simon (darkside) Jackson 2015-10-29 16:07:17 +00:00
parent 953770528d
commit a88c7a9c1b
5 changed files with 335 additions and 311 deletions

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 281614f4c0e3b7a4d9056bd377134172
folderAsset: yes
timeCreated: 1446117980
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,74 +1,79 @@
using System;
using UnityEngine;
/// Credit Ziboo
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
using System;
using UnityEngine.Events;
using UnityEngine.UI;
public class ReorderableList : MonoBehaviour
namespace UnityEngine.UI.Extensions
{
public LayoutGroup ContentLayout;
public bool IsDraggable = true;
public RectTransform DraggableArea;
public bool CloneDraggedObject = false;
public bool IsDropable = true;
public ReorderableListHandler OnElementDropped = new ReorderableListHandler();
private RectTransform _content;
private ReorderableListContent _listContent;
public RectTransform Content
[AddComponentMenu("UI/Extensions/Re-orderable list")]
public class ReorderableList : MonoBehaviour
{
get
public LayoutGroup ContentLayout;
public bool IsDraggable = true;
public RectTransform DraggableArea;
public bool CloneDraggedObject = false;
public bool IsDropable = true;
public ReorderableListHandler OnElementDropped = new ReorderableListHandler();
private RectTransform _content;
private ReorderableListContent _listContent;
public RectTransform Content
{
if (_content == null)
get
{
_content = ContentLayout.GetComponent<RectTransform>();
if (_content == null)
{
_content = ContentLayout.GetComponent<RectTransform>();
}
return _content;
}
return _content;
}
}
private void Awake()
{
if (ContentLayout == null)
private void Awake()
{
Debug.LogError("You need to have a LayoutGroup content set for the list", gameObject);
return;
if (ContentLayout == null)
{
Debug.LogError("You need to have a LayoutGroup content set for the list", gameObject);
return;
}
if (DraggableArea == null)
{
Debug.LogError("You need to set a draggable area for the list", gameObject);
return;
}
_listContent = ContentLayout.gameObject.AddComponent<ReorderableListContent>();
_listContent.Init(this);
}
if (DraggableArea == null)
#region Nested type: ReorderableListEventStruct
[Serializable]
public struct ReorderableListEventStruct
{
Debug.LogError("You need to set a draggable area for the list", gameObject);
return;
public GameObject DropedObject;
public int FromIndex;
public ReorderableList FromList;
public bool IsAClone;
public GameObject SourceObject;
public int ToIndex;
public ReorderableList ToList;
}
_listContent = ContentLayout.gameObject.AddComponent<ReorderableListContent>();
_listContent.Init(this);
#endregion
#region Nested type: ReorderableListHandler
[Serializable]
public class ReorderableListHandler : UnityEvent<ReorderableListEventStruct>
{
}
#endregion
}
#region Nested type: ReorderableListEventStruct
[Serializable]
public struct ReorderableListEventStruct
{
public GameObject DropedObject;
public int FromIndex;
public ReorderableList FromList;
public bool IsAClone;
public GameObject SourceObject;
public int ToIndex;
public ReorderableList ToList;
}
#endregion
#region Nested type: ReorderableListHandler
[Serializable]
public class ReorderableListHandler : UnityEvent<ReorderableListEventStruct>
{
}
#endregion
}

View File

@ -1,57 +1,62 @@
using System.Collections;
/// Credit Ziboo
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReorderableListContent : MonoBehaviour
namespace UnityEngine.UI.Extensions
{
private List<Transform> _cachedChildren;
private List<ReorderableListElement> _cachedListElement;
private ReorderableListElement _ele;
private ReorderableList _extList;
private RectTransform _rect;
public void OnTransformChildrenChanged()
public class ReorderableListContent : MonoBehaviour
{
StartCoroutine(RefreshChildren());
}
private List<Transform> _cachedChildren;
private List<ReorderableListElement> _cachedListElement;
private ReorderableListElement _ele;
private ReorderableList _extList;
private RectTransform _rect;
public void Init(ReorderableList extList)
{
_extList = extList;
_rect = GetComponent<RectTransform>();
_cachedChildren = new List<Transform>();
_cachedListElement = new List<ReorderableListElement>();
StartCoroutine(RefreshChildren());
}
private IEnumerator RefreshChildren()
{
//Handle new chilren
for (int i = 0; i < _rect.childCount; i++)
public void OnTransformChildrenChanged()
{
if (_cachedChildren.Contains(_rect.GetChild(i)))
continue;
//Get or Create ReorderableListElement
_ele = _rect.GetChild(i).gameObject.GetComponent<ReorderableListElement>() ??
_rect.GetChild(i).gameObject.AddComponent<ReorderableListElement>();
_ele.Init(_extList);
_cachedChildren.Add(_rect.GetChild(i));
_cachedListElement.Add(_ele);
StartCoroutine(RefreshChildren());
}
//HACK a little hack, if I don't wait one frame I don't have the right deleted children
yield return 0;
//Remove deleted child
for (int i = _cachedChildren.Count - 1; i >= 0; i--)
public void Init(ReorderableList extList)
{
if (_cachedChildren[i] == null)
_extList = extList;
_rect = GetComponent<RectTransform>();
_cachedChildren = new List<Transform>();
_cachedListElement = new List<ReorderableListElement>();
StartCoroutine(RefreshChildren());
}
private IEnumerator RefreshChildren()
{
//Handle new chilren
for (int i = 0; i < _rect.childCount; i++)
{
_cachedChildren.RemoveAt(i);
_cachedListElement.RemoveAt(i);
if (_cachedChildren.Contains(_rect.GetChild(i)))
continue;
//Get or Create ReorderableListElement
_ele = _rect.GetChild(i).gameObject.GetComponent<ReorderableListElement>() ??
_rect.GetChild(i).gameObject.AddComponent<ReorderableListElement>();
_ele.Init(_extList);
_cachedChildren.Add(_rect.GetChild(i));
_cachedListElement.Add(_ele);
}
//HACK a little hack, if I don't wait one frame I don't have the right deleted children
yield return 0;
//Remove deleted child
for (int i = _cachedChildren.Count - 1; i >= 0; i--)
{
if (_cachedChildren[i] == null)
{
_cachedChildren.RemoveAt(i);
_cachedListElement.RemoveAt(i);
}
}
}
}

View File

@ -1,29 +1,29 @@
using System;
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
/// Credit Ziboo
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
public class ReorderableListDebug : MonoBehaviour
namespace UnityEngine.UI.Extensions
{
public Text DebugLabel;
void Awake()
public class ReorderableListDebug : MonoBehaviour
{
foreach (var list in FindObjectsOfType<ReorderableList>())
public Text DebugLabel;
void Awake()
{
list.OnElementDropped.AddListener(ElementDropped);
foreach (var list in FindObjectsOfType<ReorderableList>())
{
list.OnElementDropped.AddListener(ElementDropped);
}
}
private void ElementDropped(ReorderableList.ReorderableListEventStruct droppedStruct)
{
DebugLabel.text = "";
DebugLabel.text += "Dropped Object: " + droppedStruct.DropedObject.name + "\n";
DebugLabel.text += "Is Clone ?: " + droppedStruct.IsAClone + "\n";
if (droppedStruct.IsAClone)
DebugLabel.text += "Source Object: " + droppedStruct.SourceObject.name + "\n";
DebugLabel.text += string.Format("From {0} at Index {1} \n", droppedStruct.FromList.name, droppedStruct.FromIndex);
DebugLabel.text += string.Format("To {0} at Index {1} \n", droppedStruct.ToList.name, droppedStruct.ToIndex);
}
}
private void ElementDropped(ReorderableList.ReorderableListEventStruct droppedStruct)
{
DebugLabel.text = "";
DebugLabel.text += "Dropped Object: " + droppedStruct.DropedObject.name + "\n";
DebugLabel.text += "Is Clone ?: " + droppedStruct.IsAClone + "\n";
if (droppedStruct.IsAClone)
DebugLabel.text += "Source Object: " + droppedStruct.SourceObject.name + "\n";
DebugLabel.text += string.Format("From {0} at Index {1} \n", droppedStruct.FromList.name,droppedStruct.FromIndex);
DebugLabel.text += string.Format("To {0} at Index {1} \n", droppedStruct.ToList.name,droppedStruct.ToIndex);
}
}
}

View File

@ -1,209 +1,214 @@
using System.Collections.Generic;
using UnityEngine;
/// Credit Ziboo
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof (RectTransform))]
public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
namespace UnityEngine.UI.Extensions
{
private readonly List<RaycastResult> _raycastResults = new List<RaycastResult>();
private ReorderableList _currentReorderableListRaycasted;
private RectTransform _draggingObject;
private LayoutElement _draggingObjectLE;
private Vector2 _draggingObjectOriginalSize;
private RectTransform _fakeElement;
private LayoutElement _fakeElementLE;
private int _fromIndex;
private bool _isDragging;
private RectTransform _rect;
private ReorderableList _reorderableList;
#region IBeginDragHandler Members
public void OnBeginDrag(PointerEventData eventData)
[RequireComponent(typeof(RectTransform))]
public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
if (_reorderableList == null)
return;
private readonly List<RaycastResult> _raycastResults = new List<RaycastResult>();
private ReorderableList _currentReorderableListRaycasted;
private RectTransform _draggingObject;
private LayoutElement _draggingObjectLE;
private Vector2 _draggingObjectOriginalSize;
private RectTransform _fakeElement;
private LayoutElement _fakeElementLE;
private int _fromIndex;
private bool _isDragging;
private RectTransform _rect;
private ReorderableList _reorderableList;
//Can't drag, return...
if (!_reorderableList.IsDraggable)
{
_draggingObject = null;
return;
}
#region IBeginDragHandler Members
//If CloneDraggedObject just set draggingObject to this gameobject
if (_reorderableList.CloneDraggedObject == false)
public void OnBeginDrag(PointerEventData eventData)
{
_draggingObject = _rect;
_fromIndex = _rect.GetSiblingIndex();
}
if (_reorderableList == null)
return;
//Can't drag, return...
if (!_reorderableList.IsDraggable)
{
_draggingObject = null;
return;
}
//If CloneDraggedObject just set draggingObject to this gameobject
if (_reorderableList.CloneDraggedObject == false)
{
_draggingObject = _rect;
_fromIndex = _rect.GetSiblingIndex();
}
//Else Duplicate
else
{
GameObject clone = Instantiate(gameObject);
_draggingObject = clone.GetComponent<RectTransform>();
}
//Put _dragging object into the draggin area
_draggingObjectOriginalSize = gameObject.GetComponent<RectTransform>().rect.size;
_draggingObjectLE = _draggingObject.GetComponent<LayoutElement>();
_draggingObject.SetParent(_reorderableList.DraggableArea, false);
_draggingObject.SetAsLastSibling();
//Create a fake element for previewing placement
_fakeElement = new GameObject("Fake").AddComponent<RectTransform>();
_fakeElementLE = _fakeElement.gameObject.AddComponent<LayoutElement>();
RefreshSizes();
_isDragging = true;
}
#endregion
#region IDragHandler Members
public void OnDrag(PointerEventData eventData)
{
if (!_isDragging)
return;
//Set dragging object on cursor
_draggingObject.position = eventData.position;
//Check everything under the cursor to find a ReorderableList
EventSystem.current.RaycastAll(eventData, _raycastResults);
for (int i = 0; i < _raycastResults.Count; i++)
{
_currentReorderableListRaycasted = _raycastResults[i].gameObject.GetComponent<ReorderableList>();
if (_currentReorderableListRaycasted != null)
{
break;
}
}
//If nothing found or the list is not dropable, put the fake element outsite
if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false)
{
RefreshSizes();
_fakeElement.transform.SetParent(_reorderableList.DraggableArea, false);
}
//Else find the best position on the list and put fake element on the right index
else
{
if (_fakeElement.parent != _currentReorderableListRaycasted)
_fakeElement.SetParent(_currentReorderableListRaycasted.Content, false);
float minDistance = float.PositiveInfinity;
int targetIndex = 0;
float dist = 0;
for (int j = 0; j < _currentReorderableListRaycasted.Content.childCount; j++)
{
var c = _currentReorderableListRaycasted.Content.GetChild(j).GetComponent<RectTransform>();
if (_currentReorderableListRaycasted.ContentLayout is VerticalLayoutGroup)
dist = Mathf.Abs(c.position.y - eventData.position.y);
else if (_currentReorderableListRaycasted.ContentLayout is HorizontalLayoutGroup)
dist = Mathf.Abs(c.position.x - eventData.position.x);
else if (_currentReorderableListRaycasted.ContentLayout is GridLayoutGroup)
dist = (Mathf.Abs(c.position.x - eventData.position.x) + Mathf.Abs(c.position.y - eventData.position.y));
if (dist < minDistance)
{
minDistance = dist;
targetIndex = j;
}
}
RefreshSizes();
_fakeElement.SetSiblingIndex(targetIndex);
_fakeElement.gameObject.SetActive(true);
}
}
#endregion
#region IEndDragHandler Members
public void OnEndDrag(PointerEventData eventData)
{
_isDragging = false;
if (_draggingObject != null)
{
//If we have a, ReorderableList that is dropable
//Put the dragged object into the content and at the right index
if (_currentReorderableListRaycasted != null && _currentReorderableListRaycasted.IsDropable)
{
RefreshSizes();
_draggingObject.SetParent(_currentReorderableListRaycasted.Content, false);
_draggingObject.SetSiblingIndex(_fakeElement.GetSiblingIndex());
//Send OnelementDropped Event
_reorderableList.OnElementDropped.Invoke(new ReorderableList.ReorderableListEventStruct
{
DropedObject = _draggingObject.gameObject,
IsAClone = _reorderableList.CloneDraggedObject,
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
FromList = _reorderableList,
FromIndex = _fromIndex,
ToList = _currentReorderableListRaycasted,
ToIndex = _fakeElement.GetSiblingIndex() - 1
});
}
//We don't have an ReorderableList
else
{
//If it's a clone, delete it
if (_reorderableList.CloneDraggedObject)
GameObject clone = Instantiate(gameObject);
_draggingObject = clone.GetComponent<RectTransform>();
}
//Put _dragging object into the draggin area
_draggingObjectOriginalSize = gameObject.GetComponent<RectTransform>().rect.size;
_draggingObjectLE = _draggingObject.GetComponent<LayoutElement>();
_draggingObject.SetParent(_reorderableList.DraggableArea, false);
_draggingObject.SetAsLastSibling();
//Create a fake element for previewing placement
_fakeElement = new GameObject("Fake").AddComponent<RectTransform>();
_fakeElementLE = _fakeElement.gameObject.AddComponent<LayoutElement>();
RefreshSizes();
_isDragging = true;
}
#endregion
#region IDragHandler Members
public void OnDrag(PointerEventData eventData)
{
if (!_isDragging)
return;
//Set dragging object on cursor
_draggingObject.position = eventData.position;
//Check everything under the cursor to find a ReorderableList
EventSystem.current.RaycastAll(eventData, _raycastResults);
for (int i = 0; i < _raycastResults.Count; i++)
{
_currentReorderableListRaycasted = _raycastResults[i].gameObject.GetComponent<ReorderableList>();
if (_currentReorderableListRaycasted != null)
{
Destroy(_draggingObject.gameObject);
break;
}
//Else replace the draggedObject to his first place
else
}
//If nothing found or the list is not dropable, put the fake element outsite
if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false)
{
RefreshSizes();
_fakeElement.transform.SetParent(_reorderableList.DraggableArea, false);
}
//Else find the best position on the list and put fake element on the right index
else
{
if (_fakeElement.parent != _currentReorderableListRaycasted)
_fakeElement.SetParent(_currentReorderableListRaycasted.Content, false);
float minDistance = float.PositiveInfinity;
int targetIndex = 0;
float dist = 0;
for (int j = 0; j < _currentReorderableListRaycasted.Content.childCount; j++)
{
var c = _currentReorderableListRaycasted.Content.GetChild(j).GetComponent<RectTransform>();
if (_currentReorderableListRaycasted.ContentLayout is VerticalLayoutGroup)
dist = Mathf.Abs(c.position.y - eventData.position.y);
else if (_currentReorderableListRaycasted.ContentLayout is HorizontalLayoutGroup)
dist = Mathf.Abs(c.position.x - eventData.position.x);
else if (_currentReorderableListRaycasted.ContentLayout is GridLayoutGroup)
dist = (Mathf.Abs(c.position.x - eventData.position.x) + Mathf.Abs(c.position.y - eventData.position.y));
if (dist < minDistance)
{
minDistance = dist;
targetIndex = j;
}
}
RefreshSizes();
_fakeElement.SetSiblingIndex(targetIndex);
_fakeElement.gameObject.SetActive(true);
}
}
#endregion
#region IEndDragHandler Members
public void OnEndDrag(PointerEventData eventData)
{
_isDragging = false;
if (_draggingObject != null)
{
//If we have a, ReorderableList that is dropable
//Put the dragged object into the content and at the right index
if (_currentReorderableListRaycasted != null && _currentReorderableListRaycasted.IsDropable)
{
RefreshSizes();
_draggingObject.SetParent(_reorderableList.Content, false);
_draggingObject.SetSiblingIndex(_fromIndex);
_draggingObject.SetParent(_currentReorderableListRaycasted.Content, false);
_draggingObject.SetSiblingIndex(_fakeElement.GetSiblingIndex());
//Send OnelementDropped Event
_reorderableList.OnElementDropped.Invoke(new ReorderableList.ReorderableListEventStruct
{
DropedObject = _draggingObject.gameObject,
IsAClone = _reorderableList.CloneDraggedObject,
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
FromList = _reorderableList,
FromIndex = _fromIndex,
ToList = _currentReorderableListRaycasted,
ToIndex = _fakeElement.GetSiblingIndex() - 1
});
}
//We don't have an ReorderableList
else
{
//If it's a clone, delete it
if (_reorderableList.CloneDraggedObject)
{
Destroy(_draggingObject.gameObject);
}
//Else replace the draggedObject to his first place
else
{
RefreshSizes();
_draggingObject.SetParent(_reorderableList.Content, false);
_draggingObject.SetSiblingIndex(_fromIndex);
}
}
}
//Delete fake element
if (_fakeElement != null)
Destroy(_fakeElement.gameObject);
}
//Delete fake element
if (_fakeElement != null)
Destroy(_fakeElement.gameObject);
}
#endregion
#endregion
private void RefreshSizes()
{
Vector2 size = _draggingObjectOriginalSize;
if (_currentReorderableListRaycasted != null && _currentReorderableListRaycasted.IsDropable)
private void RefreshSizes()
{
var firstChild = _currentReorderableListRaycasted.Content.GetChild(0);
if (firstChild != null)
{
size = firstChild.GetComponent<RectTransform>().rect.size;
}
}
_draggingObject.sizeDelta = size;
_fakeElementLE.preferredHeight = _draggingObjectLE.preferredHeight = size.y;
_fakeElementLE.preferredWidth = _draggingObjectLE.preferredWidth = size.x;
}
Vector2 size = _draggingObjectOriginalSize;
public void Init(ReorderableList reorderableList)
{
_reorderableList = reorderableList;
_rect = GetComponent<RectTransform>();
if (_currentReorderableListRaycasted != null && _currentReorderableListRaycasted.IsDropable)
{
var firstChild = _currentReorderableListRaycasted.Content.GetChild(0);
if (firstChild != null)
{
size = firstChild.GetComponent<RectTransform>().rect.size;
}
}
_draggingObject.sizeDelta = size;
_fakeElementLE.preferredHeight = _draggingObjectLE.preferredHeight = size.y;
_fakeElementLE.preferredWidth = _draggingObjectLE.preferredWidth = size.x;
}
public void Init(ReorderableList reorderableList)
{
_reorderableList = reorderableList;
_rect = GetComponent<RectTransform>();
}
}
}