From a58e2a0fad15b3110a07dec0348203ffedd6ec76 Mon Sep 17 00:00:00 2001 From: Sinai Date: Fri, 16 Apr 2021 02:48:49 +1000 Subject: [PATCH] Detach ScrollRect class from InfiniteScrollRect, use UIBehaviourModel. Cleanup and fix for il2cpp --- src/UI/UIFactory.cs | 25 +- .../InfiniteScroll/InfiniteScrollRect.cs | 698 ++++++++++++++---- 2 files changed, 583 insertions(+), 140 deletions(-) diff --git a/src/UI/UIFactory.cs b/src/UI/UIFactory.cs index 5095288..948b42c 100644 --- a/src/UI/UIFactory.cs +++ b/src/UI/UIFactory.cs @@ -699,7 +699,8 @@ namespace UnityExplorer.UI return dropdownObj; } - public static InfiniteScrollRect CreateInfiniteScroll(GameObject parent, string name, out GameObject content, Color? bgColor = null) + public static InfiniteScrollRect CreateInfiniteScroll(GameObject parent, string name, out GameObject uiRoot, + out GameObject content, Color? bgColor = null) { var mainObj = CreateUIObject(name, parent, new Vector2(1, 1)); mainObj.AddComponent().color = bgColor ?? new Color(0.12f, 0.12f, 0.12f); @@ -726,21 +727,25 @@ namespace UnityExplorer.UI contentRect.offsetMax = new Vector2(0f, 0f); //SetLayoutGroup(content, true, false, true, false, 0, 2, 2, 2, 2); - var infiniteScroll = mainObj.AddComponent(); - infiniteScroll.movementType = ScrollRect.MovementType.Clamped; - infiniteScroll.inertia = true; - infiniteScroll.decelerationRate = 0.135f; - infiniteScroll.scrollSensitivity = 15; - infiniteScroll.horizontal = false; - infiniteScroll.vertical = true; + var scrollRect = mainObj.AddComponent(); + scrollRect.movementType = ScrollRect.MovementType.Clamped; + scrollRect.inertia = false; + //scrollRect.decelerationRate = 0.135f; + scrollRect.scrollSensitivity = 15; + scrollRect.horizontal = false; + scrollRect.vertical = true; - infiniteScroll.viewport = viewportRect; - infiniteScroll.content = contentRect; + scrollRect.viewport = viewportRect; + scrollRect.content = contentRect; var sliderObj = SliderScrollbar.CreateSliderScrollbar(mainObj, out Slider slider); slider.direction = Slider.Direction.TopToBottom; SetLayoutElement(sliderObj, minWidth: 25, flexibleHeight: 9999); + uiRoot = mainObj; + + var infiniteScroll = new InfiniteScrollRect(scrollRect); + return infiniteScroll; } diff --git a/src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs b/src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs index 48c1224..450cc0b 100644 --- a/src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs +++ b/src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs @@ -5,15 +5,30 @@ using System.Linq; using System.Text; using UnityEngine; using UnityEngine.UI; +using UnityExplorer.UI.Models; namespace UnityExplorer.UI.Widgets.InfiniteScroll { - public class InfiniteScrollRect : ScrollRect + public class InfiniteScrollRect : UIBehaviourModel { + public InfiniteScrollRect(ScrollRect scrollRect) + { + this.scrollRect = scrollRect; + Init(); + } + public IListDataSource DataSource; - internal RectTransform PrototypeCell; + public int PoolCount => _cachedCells.Count; + public override GameObject UIRoot => scrollRect.gameObject; + + /// Use + public override void ConstructUI(GameObject parent) => throw new NotImplementedException(); + + internal ScrollRect scrollRect; + + internal RectTransform PrototypeCell; internal Slider _slider; // Cell pool @@ -22,20 +37,17 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll private List _cachedCells; private Bounds _recyclableViewBounds; - //Temps, Flags private readonly Vector3[] _corners = new Vector3[4]; private bool _recycling; private Vector2 _prevAnchoredPos; + internal Vector2 _lastScroll; - //Trackers internal int currentItemCount; //item count corresponding to the datasource. internal int topMostCellIndex, bottomMostCellIndex; //Topmost and bottommost cell in the heirarchy internal int _topMostCellColoumn, _bottomMostCellColoumn; // used for recyling in Grid layout. top-most and bottom-most coloumn - private Vector2 zeroVector = Vector2.zero; - // Flag to keep track of when we are manually setting our slider/scrollrect value directly, to avoid callback loops. - private bool internallySetting = false; + //private bool ExternallySetting = false; // external sources use this flag, it will stay true until the start of the next frame to prevent our update overwriting it. public bool ExternallySetting @@ -52,125 +64,100 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll private bool externallySetting; private float timeOfLastExternalSet; - internal new void Start() + private Vector2 zeroVector = Vector2.zero; + + public override void Init() { - // Link up the Slider and ScrollRect onValueChanged to sync them. - - _slider = this.GetComponentInChildren(); - - onValueChanged.AddListener((Vector2 val) => - { - try - { - if (internallySetting || ExternallySetting) - return; - internallySetting = true; - - SetSliderFromScrollValue(); - - internallySetting = false; - } - catch (Exception ex) - { - ExplorerCore.Log(ex); - } - }); + _slider = scrollRect.GetComponentInChildren(); _slider.onValueChanged.AddListener((float val) => { - if (internallySetting || ExternallySetting) + if (this.ExternallySetting) return; - internallySetting = true; + this.ExternallySetting = true; + + ExplorerCore.Log("Slider value changed"); // Jump to val * count (ie, 0.0 would jump to top, 1.0 would jump to bottom) var index = Math.Floor(val * DataSource.ItemCount); JumpToIndex((int)index); - internallySetting = false; + this.ExternallySetting = false; }); } - internal void SetSliderFromScrollValue() - { - // calculate where slider handle should be based on displayed range. - var range = GetDisplayedRange(); - int total = DataSource.ItemCount; - var spread = range.y - range.x; - - //var orig = _slider.value; - - if (spread >= total) - _slider.value = 0f; - else - // top-most displayed index divided by (totalCount - displayedRange) - _slider.value = (float)((decimal)range.x / (decimal)(total - spread)); - } - - internal void Update() + public override void Update() { if (externallySetting && timeOfLastExternalSet < Time.time) externallySetting = false; } + internal void OnValueChangedListener(Vector2 _) + { + if (ExternallySetting) + return; + + ExternallySetting = true; + + Vector2 dir = scrollRect.content.anchoredPosition - _prevAnchoredPos; + scrollRect.m_ContentStartPosition += ProcessValueChange(dir); + _prevAnchoredPos = scrollRect.content.anchoredPosition; + + SetSliderFromScrollValue(); + } + + /// - /// public API for Initializing when datasource is not set in controller's Awake. Make sure selfInitalize is set to false. + /// Get the start and end indexes (relative to DataSource) of the cell pool /// + public Vector2 GetDisplayedRange() + { + int max = currentItemCount; + int min = max - _cachedCells.Count; + return new Vector2(min, max); + } + + /// + /// Initialize with the provided DataSource + /// + /// public void Initialize(IListDataSource dataSource) { DataSource = dataSource; - vertical = true; - horizontal = false; + scrollRect.vertical = true; + scrollRect.horizontal = false; - _prevAnchoredPos = base.content.anchoredPosition; - onValueChanged.RemoveListener(OnValueChangedListener); + _prevAnchoredPos = scrollRect.content.anchoredPosition; + scrollRect.onValueChanged.RemoveListener(OnValueChangedListener); RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() => { - onValueChanged.AddListener(OnValueChangedListener); + scrollRect.onValueChanged.AddListener(OnValueChangedListener); })); } - /// - /// Added as a listener to the OnValueChanged event of Scroll rect. - /// Recycling entry point for recyling systems. - /// - /// scroll direction - internal void OnValueChangedListener(Vector2 normalizedPos) - { - Vector2 dir = base.content.anchoredPosition - _prevAnchoredPos; - m_ContentStartPosition += ProcessValueChange(dir); - _prevAnchoredPos = base.content.anchoredPosition; - } - - /// - ///Reloads the data. Call this if a new datasource is assigned. - /// public void ReloadData() { ReloadData(DataSource); } - /// - /// Overloaded ReloadData with dataSource param - ///Reloads the data. Call this if a new datasource is assigned. - /// public void ReloadData(IListDataSource dataSource) { - if (onValueChanged == null) + if (scrollRect.onValueChanged == null) return; - StopMovement(); + scrollRect.StopMovement(); - onValueChanged.RemoveListener(OnValueChangedListener); + scrollRect.onValueChanged.RemoveListener(OnValueChangedListener); DataSource = dataSource; RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() => - onValueChanged.AddListener(OnValueChangedListener) + scrollRect.onValueChanged.AddListener(OnValueChangedListener) )); - _prevAnchoredPos = base.content.anchoredPosition; + _prevAnchoredPos = scrollRect.content.anchoredPosition; } public void Refresh() @@ -190,17 +177,23 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll RefreshContentSize(); - //// Close, but not quite accurate enough to be useful. //internallySetting = true; //SetSliderFromScrollValue(); //internallySetting = false; } - public Vector2 GetDisplayedRange() + internal void SetSliderFromScrollValue() { - int max = currentItemCount; - int min = max - _cachedCells.Count; - return new Vector2(min, max); + // calculate where slider handle should be based on displayed range. + var range = GetDisplayedRange(); + int total = DataSource.ItemCount; + var spread = range.y - range.x; + + if (spread >= total) + _slider.value = 0f; + else + // top-most displayed index divided by (totalCount - displayedRange) + _slider.value = (float)((decimal)range.x / (decimal)(total - spread)); } public void JumpToIndex(int index) @@ -211,26 +204,25 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll var indexBuffer = (int)(_cachedCells.Count * (1 - (index / (decimal)(realCount - 1)))); - currentItemCount = index + indexBuffer;// Math.Max(index + _cachedCells.Count, currentItemCount); + currentItemCount = index + indexBuffer; currentItemCount = Math.Max(Math.Min(currentItemCount, realCount), _cachedCells.Count); Refresh(); var y = 0f; - - var displayRange = viewport.rect.height / _cellHeight; - var poolRange = content.rect.height / _cellHeight; + var displayRange = scrollRect.viewport.rect.height / _cellHeight; + var poolRange = scrollRect.content.rect.height / _cellHeight; var poolExtra = poolRange - displayRange; if (index >= realCount - poolExtra) - y = _cellHeight * (index - realCount + poolExtra); + y = _cellHeight * (index - realCount + (poolExtra * 2)); - content.anchoredPosition = new Vector2(content.anchoredPosition.x, y); + scrollRect.content.anchoredPosition = new Vector2(scrollRect.content.anchoredPosition.x, y); } public void PopulateCells() { - var width = viewport.GetComponent().rect.width; - content.sizeDelta = new Vector2(width, content.sizeDelta.y); + var width = scrollRect.viewport.GetComponent().rect.width; + scrollRect.content.sizeDelta = new Vector2(width, scrollRect.content.sizeDelta.y); int cellIndex = topMostCellIndex; var itemIndex = currentItemCount - _cachedCells.Count; @@ -246,7 +238,7 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll DataSource.SetCell(cell, itemIndex); itemIndex++; - var rect = (cell as Component).GetComponent(); + var rect = _cellPool[cellIndex]; rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); iterated++; @@ -255,17 +247,11 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll #region RECYCLING INIT - /// - /// Corotuine for initiazation. - /// Using coroutine for init because few UI stuff requires a frame to update - /// - /// callback when init done - /// > - public IEnumerator InitCoroutine(Action onInitialized) + private IEnumerator InitCoroutine(Action onInitialized) { yield return null; - SetTopAnchor(content); - content.anchoredPosition = Vector3.zero; + SetTopAnchor(scrollRect.content); + scrollRect.content.anchoredPosition = Vector3.zero; yield return null; SetRecyclingBounds(); @@ -279,38 +265,34 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll //Set content height according to no of rows RefreshContentSize(); - SetTopAnchor(content); + SetTopAnchor(scrollRect.content); onInitialized?.Invoke(); } private void RefreshContentSize() { - int noOfRows = _cachedCells.Where(it => it.Enabled).Count(); + int noOfRows = 0; + foreach (var cell in _cachedCells) + if (cell.Enabled) noOfRows++; float contentYSize = noOfRows * _cellHeight; - content.sizeDelta = new Vector2(content.sizeDelta.x, contentYSize); + scrollRect.content.sizeDelta = new Vector2(scrollRect.content.sizeDelta.x, contentYSize); } - /// - /// Sets the uppper and lower bounds for recycling cells. - /// private void SetRecyclingBounds() { - viewport.GetWorldCorners(_corners); - float threshHold = _cellHeight / 2; //RecyclingThreshold * (_corners[2].y - _corners[0].y); + scrollRect.viewport.GetCorners(_corners); + float threshHold = _cellHeight * 2; //RecyclingThreshold * (_corners[2].y - _corners[0].y); _recyclableViewBounds.min = new Vector3(_corners[0].x, _corners[0].y - threshHold); _recyclableViewBounds.max = new Vector3(_corners[2].x, _corners[2].y + threshHold); } - /// - /// Creates cell Pool for recycling, Caches ICells - /// private void CreateCellPool() { //Reseting Pool if (_cellPool != null) { - _cellPool.ForEach((RectTransform item) => Destroy(item.gameObject)); + _cellPool.ForEach((RectTransform item) => GameObject.Destroy(item.gameObject)); _cellPool.Clear(); _cachedCells.Clear(); } @@ -333,28 +315,29 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll float posY = 0; //set new cell size according to its aspect ratio - _cellWidth = content.rect.width; + _cellWidth = scrollRect.content.rect.width; _cellHeight = PrototypeCell.rect.height; //Get the required pool coverage and mininum size for the Cell pool - float requiredCoverage = viewport.rect.height + (_cellHeight * 2); + float requiredCoverage = scrollRect.viewport.rect.height + (_cellHeight * 4); //create cells untill the Pool area is covered while (currentPoolCoverage < requiredCoverage) { //Instantiate and add to Pool - RectTransform item = Instantiate(PrototypeCell.gameObject).GetComponent(); + RectTransform item = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent(); item.name = $"Cell_{_cachedCells.Count + 1}"; item.sizeDelta = new Vector2(_cellWidth, _cellHeight); _cellPool.Add(item); - item.SetParent(content, false); + item.SetParent(scrollRect.content, false); item.anchoredPosition = new Vector2(0, posY); posY = item.anchoredPosition.y - item.rect.height; currentPoolCoverage += item.rect.height; //Setting data for Cell - _cachedCells.Add(item.GetComponent()); + var cell = DataSource.CreateCell(item); + _cachedCells.Add(cell); DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize); //Update the Pool size @@ -369,21 +352,15 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll #region RECYCLING - internal Vector2 LastScroll; - - /// - /// Recyling entry point - /// - /// scroll direction - /// public Vector2 ProcessValueChange(Vector2 direction) { - if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector; + if (_recycling || _cellPool == null || _cellPool.Count == 0) + return zeroVector; //Updating Recyclable view bounds since it can change with resolution changes. SetRecyclingBounds(); - LastScroll = direction; + _lastScroll = direction; if (direction.y > 0 && _cellPool[bottomMostCellIndex].MaxY() > _recyclableViewBounds.min.y) { @@ -428,7 +405,7 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll //Content anchor position adjustment. _cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y); - content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; + scrollRect.content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; _recycling = false; return -new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y); } @@ -463,7 +440,7 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll } _cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y); - content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; + scrollRect.content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; _recycling = false; return new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y); } @@ -494,4 +471,465 @@ namespace UnityExplorer.UI.Widgets.InfiniteScroll #endregion } + //public class InfiniteScrollRect : ScrollRect + //{ + + // public IListDataSource DataSource; + + // internal RectTransform PrototypeCell; + // internal Slider _slider; + + // // Cell pool + // private float _cellWidth, _cellHeight; + // private List _cellPool; + // private List _cachedCells; + // private Bounds _recyclableViewBounds; + + // private readonly Vector3[] _corners = new Vector3[4]; + // private bool _recycling; + // private Vector2 _prevAnchoredPos; + // internal Vector2 _lastScroll; + + // internal int currentItemCount; //item count corresponding to the datasource. + // internal int topMostCellIndex, bottomMostCellIndex; //Topmost and bottommost cell in the heirarchy + // internal int _topMostCellColoumn, _bottomMostCellColoumn; // used for recyling in Grid layout. top-most and bottom-most coloumn + + // // Flag to keep track of when we are manually setting our slider/scrollrect value directly, to avoid callback loops. + // private bool internallySetting = false; + + // // external sources use this flag, it will stay true until the start of the next frame to prevent our update overwriting it. + // public bool ExternallySetting + // { + // get => externallySetting; + // internal set + // { + // if (externallySetting == value) + // return; + // timeOfLastExternalSet = Time.time; + // externallySetting = true; + // } + // } + // private bool externallySetting; + // private float timeOfLastExternalSet; + + // private Vector2 zeroVector = Vector2.zero; + + // public int PoolCount => _cachedCells.Count; + + // #region MONOBEHAVIOUR + + // internal new void Start() + // { + // // Link up the Slider and ScrollRect onValueChanged to sync them. + + // _slider = this.GetComponentInChildren(); + + // onValueChanged.AddListener((Vector2 val) => + // { + // try + // { + // if (internallySetting || ExternallySetting) + // return; + // internallySetting = true; + + // SetSliderFromScrollValue(); + + // internallySetting = false; + // } + // catch (Exception ex) + // { + // ExplorerCore.Log(ex); + // } + // }); + + // _slider.onValueChanged.AddListener((float val) => + // { + // if (internallySetting || ExternallySetting) + // return; + // internallySetting = true; + + // // Jump to val * count (ie, 0.0 would jump to top, 1.0 would jump to bottom) + // var index = Math.Floor(val * DataSource.ItemCount); + // JumpToIndex((int)index); + + // internallySetting = false; + // }); + // } + + // internal void Update() + // { + // if (externallySetting && timeOfLastExternalSet < Time.time) + // externallySetting = false; + // } + + // #endregion + + // #region LISTENERS + + // internal void OnValueChangedListener(Vector2 normalizedPos) + // { + // Vector2 dir = base.content.anchoredPosition - _prevAnchoredPos; + // m_ContentStartPosition += ProcessValueChange(dir); + // _prevAnchoredPos = base.content.anchoredPosition; + // } + + // #endregion + + // public Vector2 GetDisplayedRange() + // { + // int max = currentItemCount; + // int min = max - _cachedCells.Count; + // return new Vector2(min, max); + // } + + // public void Initialize(IListDataSource dataSource) + // { + // DataSource = dataSource; + + // vertical = true; + // horizontal = false; + + // _prevAnchoredPos = base.content.anchoredPosition; + // onValueChanged.RemoveListener(OnValueChangedListener); + + // RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() => + // { + // onValueChanged.AddListener(OnValueChangedListener); + // })); + // } + + // public void ReloadData() + // { + // ReloadData(DataSource); + // } + + // public void ReloadData(IListDataSource dataSource) + // { + // if (onValueChanged == null) + // return; + + // StopMovement(); + + // onValueChanged.RemoveListener(OnValueChangedListener); + + // DataSource = dataSource; + + // RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() => + // onValueChanged.AddListener(OnValueChangedListener) + // )); + + // _prevAnchoredPos = base.content.anchoredPosition; + // } + + // public void Refresh() + // { + // if (DataSource == null || _cellPool == null) + // return; + + // int count = DataSource.ItemCount; + // if (currentItemCount > count) + // currentItemCount = Math.Max(count, _cellPool.Count); + + // SetRecyclingBounds(); + // RecycleBottomToTop(); + // RecycleTopToBottom(); + + // PopulateCells(); + + // RefreshContentSize(); + + // // Close, but not quite accurate enough to be useful. + // internallySetting = true; + // SetSliderFromScrollValue(); + // internallySetting = false; + // } + + // internal void SetSliderFromScrollValue() + // { + // // calculate where slider handle should be based on displayed range. + // var range = GetDisplayedRange(); + // int total = DataSource.ItemCount; + // var spread = range.y - range.x; + + // //var orig = _slider.value; + + // if (spread >= total) + // _slider.value = 0f; + // else + // // top-most displayed index divided by (totalCount - displayedRange) + // _slider.value = (float)((decimal)range.x / (decimal)(total - spread)); + // } + + // public void JumpToIndex(int index) + // { + // var realCount = DataSource.ItemCount; + + // index = Math.Min(index, realCount - 1); + + // var indexBuffer = (int)(_cachedCells.Count * (1 - (index / (decimal)(realCount - 1)))); + + // currentItemCount = index + indexBuffer; + // currentItemCount = Math.Max(Math.Min(currentItemCount, realCount), _cachedCells.Count); + // Refresh(); + + // var y = 0f; + + // var displayRange = viewport.rect.height / _cellHeight; + // var poolRange = content.rect.height / _cellHeight; + // var poolExtra = poolRange - displayRange; + + // if (index >= realCount - poolExtra) + // y = _cellHeight * (index - realCount + poolExtra); + + // content.anchoredPosition = new Vector2(content.anchoredPosition.x, y); + // } + + // public void PopulateCells() + // { + // var width = viewport.GetComponent().rect.width; + // content.sizeDelta = new Vector2(width, content.sizeDelta.y); + + // int cellIndex = topMostCellIndex; + // var itemIndex = currentItemCount - _cachedCells.Count; + // int iterated = 0; + // while (iterated < _cachedCells.Count) + // { + // var cell = _cachedCells[cellIndex]; + // cellIndex++; + // if (cellIndex < 0) + // continue; + // if (cellIndex >= _cachedCells.Count) + // cellIndex = 0; + // DataSource.SetCell(cell, itemIndex); + // itemIndex++; + + // var rect = _cellPool[cellIndex].GetComponent(); + // rect.sizeDelta = new Vector2(width, rect.sizeDelta.y); + + // iterated++; + // } + // } + + // #region RECYCLING INIT + + // private IEnumerator InitCoroutine(Action onInitialized) + // { + // yield return null; + // SetTopAnchor(content); + // content.anchoredPosition = Vector3.zero; + + // yield return null; + // SetRecyclingBounds(); + + // //Cell Poool + // CreateCellPool(); + // currentItemCount = _cellPool.Count; + // topMostCellIndex = 0; + // bottomMostCellIndex = _cellPool.Count - 1; + + // //Set content height according to no of rows + // RefreshContentSize(); + + // SetTopAnchor(content); + + // onInitialized?.Invoke(); + // } + + // private void RefreshContentSize() + // { + // int noOfRows = _cachedCells.Where(it => it.Enabled).Count(); + // float contentYSize = noOfRows * _cellHeight; + // content.sizeDelta = new Vector2(content.sizeDelta.x, contentYSize); + // } + + // private void SetRecyclingBounds() + // { + // viewport.GetWorldCorners(_corners); + // float threshHold = _cellHeight / 2; //RecyclingThreshold * (_corners[2].y - _corners[0].y); + // _recyclableViewBounds.min = new Vector3(_corners[0].x, _corners[0].y - threshHold); + // _recyclableViewBounds.max = new Vector3(_corners[2].x, _corners[2].y + threshHold); + // } + + // private void CreateCellPool() + // { + // //Reseting Pool + // if (_cellPool != null) + // { + // _cellPool.ForEach((RectTransform item) => Destroy(item.gameObject)); + // _cellPool.Clear(); + // _cachedCells.Clear(); + // } + // else + // { + // _cachedCells = new List(); + // _cellPool = new List(); + // } + + // //Set the prototype cell active and set cell anchor as top + // PrototypeCell.gameObject.SetActive(true); + // SetTopAnchor(PrototypeCell); + + // //Reset + // _topMostCellColoumn = _bottomMostCellColoumn = 0; + + // //Temps + // float currentPoolCoverage = 0; + // int poolSize = 0; + // float posY = 0; + + // //set new cell size according to its aspect ratio + // _cellWidth = content.rect.width; + // _cellHeight = PrototypeCell.rect.height; + + // //Get the required pool coverage and mininum size for the Cell pool + // float requiredCoverage = viewport.rect.height + (_cellHeight * 2); + + // //create cells untill the Pool area is covered + // while (currentPoolCoverage < requiredCoverage) + // { + // //Instantiate and add to Pool + // RectTransform item = Instantiate(PrototypeCell.gameObject).GetComponent(); + // item.name = $"Cell_{_cachedCells.Count + 1}"; + // item.sizeDelta = new Vector2(_cellWidth, _cellHeight); + // _cellPool.Add(item); + // item.SetParent(content, false); + + // item.anchoredPosition = new Vector2(0, posY); + // posY = item.anchoredPosition.y - item.rect.height; + // currentPoolCoverage += item.rect.height; + + // //Setting data for Cell + // _cachedCells.Add(item.GetComponent()); + // DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize); + + // //Update the Pool size + // poolSize++; + // } + + // //Deactivate prototype cell if it is not a prefab(i.e it's present in scene) + // if (PrototypeCell.gameObject.scene.IsValid()) + // PrototypeCell.gameObject.SetActive(false); + // } + // #endregion + + // #region RECYCLING + + // public Vector2 ProcessValueChange(Vector2 direction) + // { + // if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector; + + // //Updating Recyclable view bounds since it can change with resolution changes. + // SetRecyclingBounds(); + + // _lastScroll = direction; + + // if (direction.y > 0 && _cellPool[bottomMostCellIndex].MaxY() > _recyclableViewBounds.min.y) + // { + // return RecycleTopToBottom(); + // } + // else if (direction.y < 0 && _cellPool[topMostCellIndex].MinY() < _recyclableViewBounds.max.y) + // { + // return RecycleBottomToTop(); + // } + + // return zeroVector; + // } + + // /// + // /// Recycles cells from top to bottom in the List heirarchy + // /// + // private Vector2 RecycleTopToBottom() + // { + // _recycling = true; + + // int n = 0; + // float posY; + + // //to determine if content size needs to be updated + // //Recycle until cell at Top is avaiable and current item count smaller than datasource + // while (_cellPool[topMostCellIndex].MinY() > _recyclableViewBounds.max.y && currentItemCount < DataSource.ItemCount) + // { + // //Move top cell to bottom + // posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellPool[bottomMostCellIndex].sizeDelta.y; + // _cellPool[topMostCellIndex].anchoredPosition = new Vector2(_cellPool[topMostCellIndex].anchoredPosition.x, posY); + + // //Cell for row at + // DataSource.SetCell(_cachedCells[topMostCellIndex], currentItemCount); + + // //set new indices + // bottomMostCellIndex = topMostCellIndex; + // topMostCellIndex = (topMostCellIndex + 1) % _cellPool.Count; + + // currentItemCount++; + // n++; + // } + + // //Content anchor position adjustment. + // _cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y); + // content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; + // _recycling = false; + // return -new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y); + // } + + // /// + // /// Recycles cells from bottom to top in the List heirarchy + // /// + // private Vector2 RecycleBottomToTop() + // { + // _recycling = true; + + // int n = 0; + // float posY = 0; + + // //to determine if content size needs to be updated + // //Recycle until cell at bottom is avaiable and current item count is greater than cellpool size + // while (_cellPool[bottomMostCellIndex].MaxY() < _recyclableViewBounds.min.y && currentItemCount > _cellPool.Count) + // { + // //Move bottom cell to top + // posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellPool[topMostCellIndex].sizeDelta.y; + // _cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(_cellPool[bottomMostCellIndex].anchoredPosition.x, posY); + // n++; + + // currentItemCount--; + + // //Cell for row at + // DataSource.SetCell(_cachedCells[bottomMostCellIndex], currentItemCount - _cellPool.Count); + + // //set new indices + // topMostCellIndex = bottomMostCellIndex; + // bottomMostCellIndex = (bottomMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count; + // } + + // _cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y); + // content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y; + // _recycling = false; + // return new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y); + // } + + // #endregion + + // #region HELPERS + + // /// + // /// Anchoring cell and content rect transforms to top preset. Makes repositioning easy. + // /// + // /// + // private void SetTopAnchor(RectTransform rectTransform) + // { + // //Saving to reapply after anchoring. Width and height changes if anchoring is change. + // float width = rectTransform.rect.width; + // float height = rectTransform.rect.height; + + // //Setting top anchor + // rectTransform.anchorMin = new Vector2(0.5f, 1); + // rectTransform.anchorMax = new Vector2(0.5f, 1); + // rectTransform.pivot = new Vector2(0.5f, 1); + + // //Reapply size + // rectTransform.sizeDelta = new Vector2(width, height); + // } + + // #endregion + //} + }