diff --git a/Scripts/ReorderableList/ReorderableList.cs b/Scripts/ReorderableList/ReorderableList.cs new file mode 100644 index 0000000..75d0c7e --- /dev/null +++ b/Scripts/ReorderableList/ReorderableList.cs @@ -0,0 +1,52 @@ +using System; +using UnityEngine; +using UnityEngine.Events; + +public class ReorderableList : MonoBehaviour +{ + public RectTransform Content; + + public bool IsDraggable = true; + public RectTransform DraggableArea; + public bool CloneDraggedObject = false; + + public bool IsDropable = true; + + + public ReorderableListHandler OnElementDropped = new ReorderableListHandler(); + + private ReorderableListContent _listContent; + + private void Awake() + { + if (Content == null) + { + Debug.LogError("You need to set the content for the list", gameObject); + return; + } + if (DraggableArea == null) + { + Debug.LogError("You need to set a draggable area for the list", gameObject); + return; + } + _listContent = Content.gameObject.AddComponent(); + _listContent.Init(this); + } + + [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; + } + + [Serializable] + public class ReorderableListHandler : UnityEvent + { + } +} \ No newline at end of file diff --git a/Scripts/ReorderableList/ReorderableList.cs.meta b/Scripts/ReorderableList/ReorderableList.cs.meta new file mode 100644 index 0000000..031c85d --- /dev/null +++ b/Scripts/ReorderableList/ReorderableList.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6b333d67eb08d464d823874f6a1666c2 +timeCreated: 1446072130 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ReorderableList/ReorderableList.unity b/Scripts/ReorderableList/ReorderableList.unity new file mode 100644 index 0000000..a684dec Binary files /dev/null and b/Scripts/ReorderableList/ReorderableList.unity differ diff --git a/Scripts/ReorderableList/ReorderableList.unity.meta b/Scripts/ReorderableList/ReorderableList.unity.meta new file mode 100644 index 0000000..323ff48 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableList.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9b8d590979bc3264ab9a7df11a0e8c3c +timeCreated: 1446061891 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ReorderableList/ReorderableListContent.cs b/Scripts/ReorderableList/ReorderableListContent.cs new file mode 100644 index 0000000..a9df741 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListContent.cs @@ -0,0 +1,58 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ReorderableListContent : MonoBehaviour +{ + private List _cachedChildren; + private List _cachedListElement; + private ReorderableListElement _ele; + private ReorderableList _extList; + private RectTransform _rect; + + public void OnTransformChildrenChanged() + { + StartCoroutine(RefreshChildren()); + } + + public void Init(ReorderableList extList) + { + _extList = extList; + _rect = GetComponent(); + _cachedChildren = new List(); + _cachedListElement = new List(); + + StartCoroutine(RefreshChildren()); + } + + private IEnumerator RefreshChildren() + { + //Handle new chilren + for (int i = 0; i < _rect.childCount; i++) + { + if (_cachedChildren.Contains(_rect.GetChild(i))) + continue; + + //Get or Create ReorderableListElement + _ele = _rect.GetChild(i).gameObject.GetComponent() ?? + _rect.GetChild(i).gameObject.AddComponent(); + _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); + } + } + } +} \ No newline at end of file diff --git a/Scripts/ReorderableList/ReorderableListContent.cs.meta b/Scripts/ReorderableList/ReorderableListContent.cs.meta new file mode 100644 index 0000000..0210868 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListContent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 252dd148b2c1dbe40b7d938a553e3caf +timeCreated: 1446062045 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ReorderableList/ReorderableListDebug.cs b/Scripts/ReorderableList/ReorderableListDebug.cs new file mode 100644 index 0000000..ad9db4e --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListDebug.cs @@ -0,0 +1,29 @@ +using System; +using UnityEngine; +using System.Collections; +using UnityEngine.UI; + +public class ReorderableListDebug : MonoBehaviour +{ + + public Text DebugLabel; + + void Awake() + { + foreach (var list in FindObjectsOfType()) + { + 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); + } +} diff --git a/Scripts/ReorderableList/ReorderableListDebug.cs.meta b/Scripts/ReorderableList/ReorderableListDebug.cs.meta new file mode 100644 index 0000000..8a31112 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListDebug.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 86c224aef3e999140b78d1d7135ba33f +timeCreated: 1446072313 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ReorderableList/ReorderableListElement.cs b/Scripts/ReorderableList/ReorderableListElement.cs new file mode 100644 index 0000000..397f5a2 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListElement.cs @@ -0,0 +1,159 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +[RequireComponent(typeof (RectTransform))] +public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler +{ + private readonly List _raycastResults = new List(); + private ReorderableList _currentReorderableListRaycasted; + private RectTransform _draggingObject; + private ReorderableList _reorderableList; + private RectTransform _fakeElement; + private int _fromIndex; + private bool _isDragging; + private RectTransform _rect; + + + public void OnBeginDrag(PointerEventData eventData) + { + 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(); + _draggingObject.sizeDelta = gameObject.GetComponent().sizeDelta; + } + + //Put _dragging object into the draggin area + _draggingObject.SetParent(_reorderableList.DraggableArea, false); + _draggingObject.SetAsLastSibling(); + + //Create a fake element for previewing placement + _fakeElement = new GameObject("Fake").AddComponent(); + _fakeElement.gameObject.AddComponent().preferredHeight = _rect.rect.height; + + + _isDragging = true; + } + + 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(); + if (_currentReorderableListRaycasted != null) + { + break; + } + } + + //If nothing found or the list is not dropable, put the fake element outsite + if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false) + { + _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; + for (int j = 0; j < _currentReorderableListRaycasted.Content.childCount; j++) + { + var c = _currentReorderableListRaycasted.Content.GetChild(j).GetComponent(); + float dist = Mathf.Abs(c.position.y - eventData.position.y); + + if (dist < minDistance) + { + minDistance = dist; + targetIndex = j; + } + } + + _fakeElement.SetSiblingIndex(targetIndex); + _fakeElement.gameObject.SetActive(true); + } + } + + 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) + { + _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 + { + _draggingObject.SetParent(_reorderableList.Content, false); + _draggingObject.SetSiblingIndex(_fromIndex); + } + } + } + + //Delete fake element + if (_fakeElement != null) + Destroy(_fakeElement.gameObject); + } + + public void Init(ReorderableList reorderableList) + { + _reorderableList = reorderableList; + _rect = GetComponent(); + } +} \ No newline at end of file diff --git a/Scripts/ReorderableList/ReorderableListElement.cs.meta b/Scripts/ReorderableList/ReorderableListElement.cs.meta new file mode 100644 index 0000000..58605e8 --- /dev/null +++ b/Scripts/ReorderableList/ReorderableListElement.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 916e98f1b982a9a4082fcc45c87b66c5 +timeCreated: 1446072130 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: