608 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			608 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | //#define PROFILE | |||
|  | 
 | |||
|  | namespace SRF.UI.Layout | |||
|  | { | |||
|  |     using System; | |||
|  |     using Internal; | |||
|  |     using UnityEngine; | |||
|  |     using UnityEngine.Events; | |||
|  |     using UnityEngine.EventSystems; | |||
|  |     using UnityEngine.UI; | |||
|  | 
 | |||
|  |     public interface IVirtualView | |||
|  |     { | |||
|  |         void SetDataContext(object data); | |||
|  |     } | |||
|  | 
 | |||
|  |     /// <summary> | |||
|  |     /// </summary> | |||
|  |     [AddComponentMenu(ComponentMenuPaths.VirtualVerticalLayoutGroup)] | |||
|  |     public class VirtualVerticalLayoutGroup : LayoutGroup, IPointerClickHandler | |||
|  |     { | |||
|  |         private readonly SRList<object> _itemList = new SRList<object>(); | |||
|  |         private readonly SRList<int> _visibleItemList = new SRList<int>(); | |||
|  | 
 | |||
|  |         private bool _isDirty = false; | |||
|  |         private SRList<Row> _rowCache = new SRList<Row>(); | |||
|  |         private ScrollRect _scrollRect; | |||
|  |         private int _selectedIndex; | |||
|  |         private object _selectedItem; | |||
|  | 
 | |||
|  |         [SerializeField] private SelectedItemChangedEvent _selectedItemChanged; | |||
|  | 
 | |||
|  |         private int _visibleItemCount; | |||
|  |         private SRList<Row> _visibleRows = new SRList<Row>(); | |||
|  |         public StyleSheet AltRowStyleSheet; | |||
|  |         public bool EnableSelection = true; | |||
|  |         public RectTransform ItemPrefab; | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// Rows to show above and below the visible rect to reduce pop-in | |||
|  |         /// </summary> | |||
|  |         public int RowPadding = 2; | |||
|  | 
 | |||
|  |         public StyleSheet RowStyleSheet; | |||
|  |         public StyleSheet SelectedRowStyleSheet; | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// Spacing to add between rows | |||
|  |         /// </summary> | |||
|  |         public float Spacing; | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// If true, the scroll view will stick to the last element when fully scrolled to the bottom and an item is added | |||
|  |         /// </summary> | |||
|  |         public bool StickToBottom = true; | |||
|  | 
 | |||
|  |         public SelectedItemChangedEvent SelectedItemChanged | |||
|  |         { | |||
|  |             get { return _selectedItemChanged; } | |||
|  |             set { _selectedItemChanged = value; } | |||
|  |         } | |||
|  | 
 | |||
|  |         public object SelectedItem | |||
|  |         { | |||
|  |             get { return _selectedItem; } | |||
|  |             set | |||
|  |             { | |||
|  |                 if (_selectedItem == value || !EnableSelection) | |||
|  |                 { | |||
|  |                     return; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 var newSelectedIndex = value == null ? -1 : _itemList.IndexOf(value); | |||
|  | 
 | |||
|  |                 // Ensure that the new selected item is present in the item list | |||
|  |                 if (value != null && newSelectedIndex < 0) | |||
|  |                 { | |||
|  |                     throw new InvalidOperationException("Cannot select item not present in layout"); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // Invalidate old selected item row | |||
|  |                 if (_selectedItem != null) | |||
|  |                 { | |||
|  |                     InvalidateItem(_selectedIndex); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 _selectedItem = value; | |||
|  |                 _selectedIndex = newSelectedIndex; | |||
|  | 
 | |||
|  |                 // Invalidate the newly selected item | |||
|  |                 if (_selectedItem != null) | |||
|  |                 { | |||
|  |                     InvalidateItem(_selectedIndex); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 SetDirty(); | |||
|  | 
 | |||
|  |                 if (_selectedItemChanged != null) | |||
|  |                 { | |||
|  |                     _selectedItemChanged.Invoke(_selectedItem); | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         public override float minHeight | |||
|  |         { | |||
|  |             get { return _itemList.Count*ItemHeight + padding.top + padding.bottom + Spacing*_itemList.Count; } | |||
|  |         } | |||
|  | 
 | |||
|  |         public void OnPointerClick(PointerEventData eventData) | |||
|  |         { | |||
|  |             if (!EnableSelection) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             var hitObject = eventData.pointerPressRaycast.gameObject; | |||
|  | 
 | |||
|  |             if (hitObject == null) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             var hitPos = hitObject.transform.position; | |||
|  |             var localPos = rectTransform.InverseTransformPoint(hitPos); | |||
|  |             var row = Mathf.FloorToInt(Mathf.Abs(localPos.y)/ItemHeight); | |||
|  | 
 | |||
|  |             if (row >= 0 && row < _itemList.Count) | |||
|  |             { | |||
|  |                 SelectedItem = _itemList[row]; | |||
|  |             } | |||
|  |             else | |||
|  |             { | |||
|  |                 SelectedItem = null; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void Awake() | |||
|  |         { | |||
|  |             base.Awake(); | |||
|  | 
 | |||
|  |             ScrollRect.onValueChanged.AddListener(OnScrollRectValueChanged); | |||
|  | 
 | |||
|  |             var view = ItemPrefab.GetComponent(typeof (IVirtualView)); | |||
|  | 
 | |||
|  |             if (view == null) | |||
|  |             { | |||
|  |                 Debug.LogWarning( | |||
|  |                     "[VirtualVerticalLayoutGroup] ItemPrefab does not have a component inheriting from IVirtualView, so no data binding can occur"); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private void OnScrollRectValueChanged(Vector2 d) | |||
|  |         { | |||
|  |             if (d.y < 0 || d.y > 1) | |||
|  |             { | |||
|  |                 _scrollRect.verticalNormalizedPosition = Mathf.Clamp01(d.y); | |||
|  |             } | |||
|  | 
 | |||
|  |             //CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); | |||
|  |             SetDirty(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void Start() | |||
|  |         { | |||
|  |             base.Start(); | |||
|  |             ScrollUpdate(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected override void OnEnable() | |||
|  |         { | |||
|  |             base.OnEnable(); | |||
|  |             SetDirty(); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected void Update() | |||
|  |         { | |||
|  |             if (!AlignBottom && !AlignTop) | |||
|  |             { | |||
|  |                 Debug.LogWarning("[VirtualVerticalLayoutGroup] Only Lower or Upper alignment is supported.", this); | |||
|  |                 childAlignment = TextAnchor.UpperLeft; | |||
|  |             } | |||
|  | 
 | |||
|  |             if (SelectedItem != null && !_itemList.Contains(SelectedItem)) | |||
|  |             { | |||
|  |                 SelectedItem = null; | |||
|  |             } | |||
|  | 
 | |||
|  |             if (_isDirty) | |||
|  |             { | |||
|  |                 _isDirty = false; | |||
|  |                 ScrollUpdate(); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// Invalidate a single row (before removing, or changing selection status) | |||
|  |         /// </summary> | |||
|  |         /// <param name="itemIndex"></param> | |||
|  |         protected void InvalidateItem(int itemIndex) | |||
|  |         { | |||
|  |             if (!_visibleItemList.Contains(itemIndex)) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             _visibleItemList.Remove(itemIndex); | |||
|  | 
 | |||
|  |             for (var i = 0; i < _visibleRows.Count; i++) | |||
|  |             { | |||
|  |                 if (_visibleRows[i].Index == itemIndex) | |||
|  |                 { | |||
|  |                     RecycleRow(_visibleRows[i]); | |||
|  |                     _visibleRows.RemoveAt(i); | |||
|  |                     break; | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         /// <summary> | |||
|  |         /// After removing or inserting a row, ensure that the cached indexes (used for layout) match up | |||
|  |         /// with the item index in the list | |||
|  |         /// </summary> | |||
|  |         protected void RefreshIndexCache() | |||
|  |         { | |||
|  |             for (var i = 0; i < _visibleRows.Count; i++) | |||
|  |             { | |||
|  |                 _visibleRows[i].Index = _itemList.IndexOf(_visibleRows[i].Data); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         protected void ScrollUpdate() | |||
|  |         { | |||
|  |             if (!Application.isPlaying) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             //Debug.Log("[SRConsole] ScrollUpdate {0}".Fmt(Time.frameCount)); | |||
|  | 
 | |||
|  |             var pos = rectTransform.anchoredPosition; | |||
|  |             var startY = pos.y; | |||
|  | 
 | |||
|  |             var viewHeight = ((RectTransform) ScrollRect.transform).rect.height; | |||
|  | 
 | |||
|  |             // Determine the range of rows that should be visible | |||
|  |             var rowRangeLower = Mathf.FloorToInt(startY/(ItemHeight + Spacing)); | |||
|  |             var rowRangeHigher = Mathf.CeilToInt((startY + viewHeight)/(ItemHeight + Spacing)); | |||
|  | 
 | |||
|  |             // Apply padding to reduce pop-in | |||
|  |             rowRangeLower -= RowPadding; | |||
|  |             rowRangeHigher += RowPadding; | |||
|  | 
 | |||
|  |             rowRangeLower = Mathf.Max(0, rowRangeLower); | |||
|  |             rowRangeHigher = Mathf.Min(_itemList.Count, rowRangeHigher); | |||
|  | 
 | |||
|  |             var isDirty = false; | |||
|  | 
 | |||
|  | #if PROFILE | |||
|  | 			Profiler.BeginSample("Visible Rows Cull"); | |||
|  | #endif | |||
|  | 
 | |||
|  |             for (var i = 0; i < _visibleRows.Count; i++) | |||
|  |             { | |||
|  |                 var row = _visibleRows[i]; | |||
|  | 
 | |||
|  |                 // Move on if row is still visible | |||
|  |                 if (row.Index >= rowRangeLower && row.Index <= rowRangeHigher) | |||
|  |                 { | |||
|  |                     continue; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 _visibleItemList.Remove(row.Index); | |||
|  |                 _visibleRows.Remove(row); | |||
|  |                 RecycleRow(row); | |||
|  |                 isDirty = true; | |||
|  |             } | |||
|  | 
 | |||
|  | #if PROFILE | |||
|  | 			Profiler.EndSample(); | |||
|  | 			Profiler.BeginSample("Item Visible Check"); | |||
|  | #endif | |||
|  | 
 | |||
|  |             for (var i = rowRangeLower; i < rowRangeHigher; ++i) | |||
|  |             { | |||
|  |                 if (i >= _itemList.Count) | |||
|  |                 { | |||
|  |                     break; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // Move on if row is already visible | |||
|  |                 if (_visibleItemList.Contains(i)) | |||
|  |                 { | |||
|  |                     continue; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 var row = GetRow(i); | |||
|  |                 _visibleRows.Add(row); | |||
|  |                 _visibleItemList.Add(i); | |||
|  |                 isDirty = true; | |||
|  |             } | |||
|  | 
 | |||
|  | #if PROFILE | |||
|  | 			Profiler.EndSample(); | |||
|  | #endif | |||
|  | 
 | |||
|  |             // If something visible has explicitly been changed, or the visible row count has changed | |||
|  |             if (isDirty || _visibleItemCount != _visibleRows.Count) | |||
|  |             { | |||
|  |                 //Debug.Log("[SRConsole] IsDirty {0}".Fmt(Time.frameCount)); | |||
|  |                 LayoutRebuilder.MarkLayoutForRebuild(rectTransform); | |||
|  |             } | |||
|  | 
 | |||
|  |             _visibleItemCount = _visibleRows.Count; | |||
|  |         } | |||
|  | 
 | |||
|  |         public override void CalculateLayoutInputVertical() | |||
|  |         { | |||
|  |             SetLayoutInputForAxis(minHeight, minHeight, -1, 1); | |||
|  |         } | |||
|  | 
 | |||
|  |         public override void SetLayoutHorizontal() | |||
|  |         { | |||
|  |             var width = rectTransform.rect.width - padding.left - padding.right; | |||
|  | 
 | |||
|  |             // Position visible rows at 0 x | |||
|  |             for (var i = 0; i < _visibleRows.Count; i++) | |||
|  |             { | |||
|  |                 var item = _visibleRows[i]; | |||
|  | 
 | |||
|  |                 SetChildAlongAxis(item.Rect, 0, padding.left, width); | |||
|  |             } | |||
|  | 
 | |||
|  |             // Hide non-active rows to one side. More efficient than enabling/disabling them | |||
|  |             for (var i = 0; i < _rowCache.Count; i++) | |||
|  |             { | |||
|  |                 var item = _rowCache[i]; | |||
|  | 
 | |||
|  |                 SetChildAlongAxis(item.Rect, 0, -width - padding.left, width); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         public override void SetLayoutVertical() | |||
|  |         { | |||
|  |             if (!Application.isPlaying) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             //Debug.Log("[SRConsole] SetLayoutVertical {0}".Fmt(Time.frameCount)); | |||
|  | 
 | |||
|  |             // Position visible rows by the index of the item they represent | |||
|  |             for (var i = 0; i < _visibleRows.Count; i++) | |||
|  |             { | |||
|  |                 var item = _visibleRows[i]; | |||
|  | 
 | |||
|  |                 SetChildAlongAxis(item.Rect, 1, item.Index*ItemHeight + padding.top + Spacing*item.Index, ItemHeight); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private new void SetDirty() | |||
|  |         { | |||
|  |             base.SetDirty(); | |||
|  | 
 | |||
|  |             if (!IsActive()) | |||
|  |             { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             _isDirty = true; | |||
|  |             //CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); | |||
|  |         } | |||
|  | 
 | |||
|  |         [Serializable] | |||
|  |         public class SelectedItemChangedEvent : UnityEvent<object> {} | |||
|  | 
 | |||
|  |         [Serializable] | |||
|  |         private class Row | |||
|  |         { | |||
|  |             public object Data; | |||
|  |             public int Index; | |||
|  |             public RectTransform Rect; | |||
|  |             public StyleRoot Root; | |||
|  |             public IVirtualView View; | |||
|  |         } | |||
|  | 
 | |||
|  |         #region Public Data Methods | |||
|  | 
 | |||
|  |         public void AddItem(object item) | |||
|  |         { | |||
|  |             _itemList.Add(item); | |||
|  |             SetDirty(); | |||
|  | 
 | |||
|  |             if (StickToBottom && Mathf.Approximately(ScrollRect.verticalNormalizedPosition, 0f)) | |||
|  |             { | |||
|  |                 ScrollRect.normalizedPosition = new Vector2(0, 0); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         public void RemoveItem(object item) | |||
|  |         { | |||
|  |             if (SelectedItem == item) | |||
|  |             { | |||
|  |                 SelectedItem = null; | |||
|  |             } | |||
|  | 
 | |||
|  |             var index = _itemList.IndexOf(item); | |||
|  | 
 | |||
|  |             InvalidateItem(index); | |||
|  |             _itemList.Remove(item); | |||
|  | 
 | |||
|  |             RefreshIndexCache(); | |||
|  | 
 | |||
|  |             SetDirty(); | |||
|  |         } | |||
|  | 
 | |||
|  |         public void ClearItems() | |||
|  |         { | |||
|  |             for (var i = _visibleRows.Count - 1; i >= 0; i--) | |||
|  |             { | |||
|  |                 InvalidateItem(_visibleRows[i].Index); | |||
|  |             } | |||
|  | 
 | |||
|  |             _itemList.Clear(); | |||
|  |             SetDirty(); | |||
|  |         } | |||
|  | 
 | |||
|  |         #endregion | |||
|  | 
 | |||
|  |         #region Internal Properties | |||
|  | 
 | |||
|  |         private ScrollRect ScrollRect | |||
|  |         { | |||
|  |             get | |||
|  |             { | |||
|  |                 if (_scrollRect == null) | |||
|  |                 { | |||
|  |                     _scrollRect = GetComponentInParent<ScrollRect>(); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 return _scrollRect; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private bool AlignBottom | |||
|  |         { | |||
|  |             get | |||
|  |             { | |||
|  |                 return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.LowerCenter || | |||
|  |                        childAlignment == TextAnchor.LowerLeft; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private bool AlignTop | |||
|  |         { | |||
|  |             get | |||
|  |             { | |||
|  |                 return childAlignment == TextAnchor.UpperLeft || childAlignment == TextAnchor.UpperCenter || | |||
|  |                        childAlignment == TextAnchor.UpperRight; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private float _itemHeight = -1; | |||
|  | 
 | |||
|  |         private float ItemHeight | |||
|  |         { | |||
|  |             get | |||
|  |             { | |||
|  |                 if (_itemHeight <= 0) | |||
|  |                 { | |||
|  |                     var layoutElement = ItemPrefab.GetComponent(typeof (ILayoutElement)) as ILayoutElement; | |||
|  | 
 | |||
|  |                     if (layoutElement != null) | |||
|  |                     { | |||
|  |                         _itemHeight = layoutElement.preferredHeight; | |||
|  |                     } | |||
|  |                     else | |||
|  |                     { | |||
|  |                         _itemHeight = ItemPrefab.rect.height; | |||
|  |                     } | |||
|  | 
 | |||
|  |                     if (_itemHeight.ApproxZero()) | |||
|  |                     { | |||
|  |                         Debug.LogWarning( | |||
|  |                             "[VirtualVerticalLayoutGroup] ItemPrefab must have a preferred size greater than 0"); | |||
|  |                         _itemHeight = 10; | |||
|  |                     } | |||
|  |                 } | |||
|  | 
 | |||
|  |                 return _itemHeight; | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         #endregion | |||
|  | 
 | |||
|  |         #region Row Pooling and Provisioning | |||
|  | 
 | |||
|  |         private Row GetRow(int forIndex) | |||
|  |         { | |||
|  |             // If there are no rows available in the cache, create one from scratch | |||
|  |             if (_rowCache.Count == 0) | |||
|  |             { | |||
|  |                 var newRow = CreateRow(); | |||
|  |                 PopulateRow(forIndex, newRow); | |||
|  |                 return newRow; | |||
|  |             } | |||
|  | 
 | |||
|  |             var data = _itemList[forIndex]; | |||
|  | 
 | |||
|  |             Row row = null; | |||
|  |             Row altRow = null; | |||
|  | 
 | |||
|  |             // Determine if the row we're looking for is an alt row | |||
|  |             var target = forIndex%2; | |||
|  | 
 | |||
|  |             // Try and find a row which previously had this data, so we can reuse it | |||
|  |             for (var i = 0; i < _rowCache.Count; i++) | |||
|  |             { | |||
|  |                 row = _rowCache[i]; | |||
|  | 
 | |||
|  |                 // If this row previously represented this data, just use that one. | |||
|  |                 if (row.Data == data) | |||
|  |                 { | |||
|  |                     _rowCache.RemoveAt(i); | |||
|  |                     PopulateRow(forIndex, row); | |||
|  |                     break; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // Cache a row which is was the same alt state as the row we're looking for, in case | |||
|  |                 // we don't find an exact match. | |||
|  |                 if (row.Index%2 == target) | |||
|  |                 { | |||
|  |                     altRow = row; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // Didn't match, reset to null | |||
|  |                 row = null; | |||
|  |             } | |||
|  | 
 | |||
|  |             // If an exact match wasn't found, but a row with the same alt-status was found, use that one. | |||
|  |             if (row == null && altRow != null) | |||
|  |             { | |||
|  |                 _rowCache.Remove(altRow); | |||
|  |                 row = altRow; | |||
|  |                 PopulateRow(forIndex, row); | |||
|  |             } | |||
|  |             else if (row == null) | |||
|  |             { | |||
|  |                 // No match found, use the last added item in the cache | |||
|  |                 row = _rowCache.PopLast(); | |||
|  |                 PopulateRow(forIndex, row); | |||
|  |             } | |||
|  | 
 | |||
|  |             return row; | |||
|  |         } | |||
|  | 
 | |||
|  |         private void RecycleRow(Row row) | |||
|  |         { | |||
|  |             _rowCache.Add(row); | |||
|  |         } | |||
|  | 
 | |||
|  |         private void PopulateRow(int index, Row row) | |||
|  |         { | |||
|  |             row.Index = index; | |||
|  | 
 | |||
|  |             // Set data context on row | |||
|  |             row.Data = _itemList[index]; | |||
|  |             row.View.SetDataContext(_itemList[index]); | |||
|  | 
 | |||
|  |             // If we're using stylesheets | |||
|  |             if (RowStyleSheet != null || AltRowStyleSheet != null || SelectedRowStyleSheet != null) | |||
|  |             { | |||
|  |                 // If there is a selected row stylesheet, and this is the selected row, use that one | |||
|  |                 if (SelectedRowStyleSheet != null && SelectedItem == row.Data) | |||
|  |                 { | |||
|  |                     row.Root.StyleSheet = SelectedRowStyleSheet; | |||
|  |                 } | |||
|  |                 else | |||
|  |                 { | |||
|  |                     // Otherwise just use the stylesheet suitable for the row alt-status | |||
|  |                     row.Root.StyleSheet = index%2 == 0 ? RowStyleSheet : AltRowStyleSheet; | |||
|  |                 } | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private Row CreateRow() | |||
|  |         { | |||
|  |             var item = new Row(); | |||
|  | 
 | |||
|  |             var row = SRInstantiate.Instantiate(ItemPrefab); | |||
|  |             item.Rect = row; | |||
|  |             item.View = row.GetComponent(typeof (IVirtualView)) as IVirtualView; | |||
|  | 
 | |||
|  |             if (RowStyleSheet != null || AltRowStyleSheet != null || SelectedRowStyleSheet != null) | |||
|  |             { | |||
|  |                 item.Root = row.gameObject.GetComponentOrAdd<StyleRoot>(); | |||
|  |                 item.Root.StyleSheet = RowStyleSheet; | |||
|  |             } | |||
|  | 
 | |||
|  |             row.SetParent(rectTransform, false); | |||
|  | 
 | |||
|  |             return item; | |||
|  |         } | |||
|  | 
 | |||
|  |         #endregion | |||
|  |     } | |||
|  | } |