Cleanup and fix some small issues with scroll pool

This commit is contained in:
Sinai 2021-04-21 23:07:15 +10:00
parent 0a9639f8a9
commit edbb9a2882
12 changed files with 174 additions and 138 deletions

View File

@ -168,34 +168,54 @@ namespace UnityExplorer.UI.Panels
public int ItemCount => Inspector.dummyContents.Count; public int ItemCount => Inspector.dummyContents.Count;
public void OnDisableCell(CellViewHolder cell, int dataIndex)
{
if (cell.UIRoot.transform.Find("Content") is Transform existing)
existing.transform.SetParent(Inspector.dummyContentHolder.transform, false);
}
public void Init() public void Init()
{ {
var prototype = DynamicCell.CreatePrototypeCell(Scroller.UIRoot); var prototype = CellViewHolder.CreatePrototypeCell(Scroller.UIRoot);
Scroller.DataSource = this; Scroller.DataSource = this;
Scroller.Initialize(this, prototype); Scroller.Initialize(this, prototype);
} }
public ICell CreateCell(RectTransform cellTransform) => new DynamicCell(cellTransform.gameObject); public ICell CreateCell(RectTransform cellTransform) => new CellViewHolder(cellTransform.gameObject);
public void DisableCell(ICell icell, int index)
{
var root = (icell as CellViewHolder).UIRoot;
DisableContent(root);
icell.Disable();
}
public void SetCell(ICell icell, int index) public void SetCell(ICell icell, int index)
{ {
var root = (icell as CellViewHolder).UIRoot;
if (index < 0 || index >= ItemCount) if (index < 0 || index >= ItemCount)
{ {
DisableContent(root);
icell.Disable(); icell.Disable();
return; return;
} }
var root = (icell as DynamicCell).uiRoot;
var content = Inspector.dummyContents[index]; var content = Inspector.dummyContents[index];
if (content.transform.parent.ReferenceEqual(root.transform)) if (content.transform.parent.ReferenceEqual(root.transform))
return; return;
if (root.transform.Find("Content") is Transform existing) DisableContent(root);
existing.transform.SetParent(Inspector.dummyContentHolder.transform, false);
content.transform.SetParent(root.transform, false); content.transform.SetParent(root.transform, false);
} }
private void DisableContent(GameObject cellRoot)
{
if (cellRoot.transform.Find("Content") is Transform existing)
existing.transform.SetParent(Inspector.dummyContentHolder.transform, false);
}
} }
} }

View File

@ -128,26 +128,26 @@ namespace UnityExplorer.UI.Panels
Tree.RefreshData(true); Tree.RefreshData(true);
} }
private float highestRectHeight; //private float highestRectHeight;
public override void OnFinishResize(RectTransform panel) //public override void OnFinishResize(RectTransform panel)
{ //{
base.OnFinishResize(panel); // base.OnFinishResize(panel);
RuntimeProvider.Instance.StartCoroutine(DelayedRefresh(panel)); // RuntimeProvider.Instance.StartCoroutine(DelayedRefresh(panel));
} //}
private IEnumerator DelayedRefresh(RectTransform obj) //private IEnumerator DelayedRefresh(RectTransform obj)
{ //{
yield return null; // yield return null;
if (obj.rect.height > highestRectHeight) // if (obj.rect.height > highestRectHeight)
{ // {
// height increased, hard refresh required. // // height increased, hard refresh required.
highestRectHeight = obj.rect.height; // highestRectHeight = obj.rect.height;
//Tree.Scroller.ReloadData(); // //Tree.Scroller.ReloadData();
} // }
Tree.Scroller.RefreshCells(true); // Tree.Scroller.RefreshCells(true);
} //}
public override void SaveToConfigManager() public override void SaveToConfigManager()
{ {
@ -192,10 +192,6 @@ namespace UnityExplorer.UI.Panels
var filterRow = UIFactory.CreateHorizontalGroup(toolbar, "FilterGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2)); var filterRow = UIFactory.CreateHorizontalGroup(toolbar, "FilterGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0); UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0);
//// filter label
//var label = UIFactory.CreateLabel(filterRow, "FilterLabel", "Search:", TextAnchor.MiddleLeft);
//UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0);
//Filter input field //Filter input field
var inputFieldObj = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...", out InputField inputField, 13); var inputFieldObj = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...", out InputField inputField, 13);
inputField.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f); inputField.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
@ -224,23 +220,23 @@ namespace UnityExplorer.UI.Panels
// Transform Tree // Transform Tree
//var prototype = TransformCell.CreatePrototypeCell(scrollContent);
var infiniteScroll = UIFactory.CreateScrollPool(content, "TransformTree", out GameObject scrollObj, var infiniteScroll = UIFactory.CreateScrollPool(content, "TransformTree", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.15f, 0.15f, 0.15f)); out GameObject scrollContent, new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999); UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
// Prototype tree cell
//infiniteScroll.PrototypeCell = prototype.GetComponent<RectTransform>();
Tree = new TransformTree(infiniteScroll) { GetRootEntriesMethod = GetRootEntries }; Tree = new TransformTree(infiniteScroll) { GetRootEntriesMethod = GetRootEntries };
Tree.Init(); Tree.Init();
// some references // Scene Loader
highestRectHeight = mainPanelRect.rect.height;
ConstructSceneLoader();
}
private const string DEFAULT_LOAD_TEXT = "[Select a scene]";
private void ConstructSceneLoader()
{
// Scene Loader // Scene Loader
try try
{ {
@ -256,7 +252,7 @@ namespace UnityExplorer.UI.Panels
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out Dropdown allSceneDrop, "", 14, null); var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out Dropdown allSceneDrop, "", 14, null);
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0); UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0);
allSceneDrop.options.Add(new Dropdown.OptionData("[Select a scene]")); allSceneDrop.options.Add(new Dropdown.OptionData(DEFAULT_LOAD_TEXT));
foreach (var scene in SceneHandler.AllSceneNames) foreach (var scene in SceneHandler.AllSceneNames)
allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene))); allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene)));
@ -277,20 +273,41 @@ namespace UnityExplorer.UI.Panels
TryLoadScene(LoadSceneMode.Additive, allSceneDrop); TryLoadScene(LoadSceneMode.Additive, allSceneDrop);
}, new Color(0.1f, 0.3f, 0.3f)); }, new Color(0.1f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(loadAdditiveButton.gameObject, minHeight: 25, minWidth: 150); UIFactory.SetLayoutElement(loadAdditiveButton.gameObject, minHeight: 25, minWidth: 150);
var disabledColor = new Color(0.24f, 0.24f, 0.24f);
RuntimeProvider.Instance.SetColorBlock(loadButton, disabled: disabledColor);
RuntimeProvider.Instance.SetColorBlock(loadAdditiveButton, disabled: disabledColor);
loadButton.interactable = false;
loadAdditiveButton.interactable = false;
allSceneDrop.onValueChanged.AddListener((int val) =>
{
var text = allSceneDrop.options[val].text;
if (text == DEFAULT_LOAD_TEXT)
{
loadButton.interactable = false;
loadAdditiveButton.interactable = false;
}
else
{
loadButton.interactable = true;
loadAdditiveButton.interactable = true;
}
});
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
ExplorerCore.LogWarning($"Could not create the Scene Loader helper! {ex.ReflectionExToString()}"); ExplorerCore.LogWarning($"Could not create the Scene Loader helper! {ex.ReflectionExToString()}");
} }
} }
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop) private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
{ {
var text = allSceneDrop.options[allSceneDrop.value].text; var text = allSceneDrop.options[allSceneDrop.value].text;
if (text == "[Select a scene]") if (text == DEFAULT_LOAD_TEXT)
return; return;
try try

View File

@ -17,6 +17,8 @@ namespace UnityExplorer.UI.Widgets
public ButtonListSource<T> list; public ButtonListSource<T> list;
public GameObject uiRoot; public GameObject uiRoot;
private RectTransform m_rect;
public RectTransform Rect => m_rect;
public Text buttonText; public Text buttonText;
public Button button; public Button button;
@ -24,6 +26,7 @@ namespace UnityExplorer.UI.Widgets
{ {
this.list = list; this.list = list;
this.uiRoot = uiRoot; this.uiRoot = uiRoot;
this.m_rect = uiRoot.GetComponent<RectTransform>();
this.buttonText = text; this.buttonText = text;
this.button = button; this.button = button;

View File

@ -96,5 +96,7 @@ namespace UnityExplorer.UI.Widgets
SetICell.Invoke((ButtonCell<T>)cell, index); SetICell.Invoke((ButtonCell<T>)cell, index);
} }
} }
public void DisableCell(ICell cell, int index) => cell.Disable();
} }
} }

View File

@ -7,30 +7,32 @@ using UnityEngine.UI;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets
{ {
public class DynamicCell : ICell public class CellViewHolder : ICell
{ {
public DynamicCell(GameObject uiRoot) public CellViewHolder(GameObject uiRoot)
{ {
this.uiRoot = uiRoot; this.UIRoot = uiRoot;
this.m_rect = uiRoot.GetComponent<RectTransform>();
m_enabled = uiRoot.activeSelf; m_enabled = uiRoot.activeSelf;
} }
public bool Enabled => m_enabled; public bool Enabled => m_enabled;
private bool m_enabled; private bool m_enabled;
public GameObject uiRoot; public GameObject UIRoot;
public InputField input; public RectTransform Rect => m_rect;
private RectTransform m_rect;
public void Disable() public void Disable()
{ {
m_enabled = false; m_enabled = false;
uiRoot.SetActive(false); UIRoot.SetActive(false);
} }
public void Enable() public void Enable()
{ {
m_enabled = true; m_enabled = true;
uiRoot.SetActive(true); UIRoot.SetActive(true);
} }
public static RectTransform CreatePrototypeCell(GameObject parent) public static RectTransform CreatePrototypeCell(GameObject parent)
@ -42,7 +44,6 @@ namespace UnityExplorer.UI.Widgets
rect.anchorMax = new Vector2(0, 1); rect.anchorMax = new Vector2(0, 1);
rect.pivot = new Vector2(0.5f, 1); rect.pivot = new Vector2(0.5f, 1);
rect.sizeDelta = new Vector2(100, 30); rect.sizeDelta = new Vector2(100, 30);
//UIFactory.SetLayoutElement(prototype, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 9999);
prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;

View File

@ -113,7 +113,7 @@ namespace UnityExplorer.UI.Widgets
if (diff != 0.0f) if (diff != 0.0f)
{ {
//ExplorerCore.LogWarning("Height for data index " + dataIndex + " changed by " + diff); ExplorerCore.LogWarning("Height for data index " + dataIndex + " changed by " + diff);
cache.height = value; cache.height = value;
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets
{ {
@ -9,6 +10,8 @@ namespace UnityExplorer.UI.Widgets
{ {
bool Enabled { get; } bool Enabled { get; }
RectTransform Rect { get; }
void Enable(); void Enable();
void Disable(); void Disable();
} }

View File

@ -11,6 +11,7 @@ namespace UnityExplorer.UI.Widgets
int ItemCount { get; } int ItemCount { get; }
void SetCell(ICell cell, int index); void SetCell(ICell cell, int index);
void DisableCell(ICell cell, int index);
ICell CreateCell(RectTransform cellTransform); ICell CreateCell(RectTransform cellTransform);
} }

View File

@ -9,28 +9,25 @@ using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets
{ {
// TODO: there is possibly still a bug causing the content to jump around, sometimes observed when
// the pooled content height is extremely large (compared to viewport). maybe it depends on the
// top/bottom cells or something.
/// <summary> /// <summary>
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.</summary> /// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
/// </summary>
public class ScrollPool : UIBehaviourModel public class ScrollPool : UIBehaviourModel
{ {
// used to track and manage cell views //// used to track and manage cell views
public class CachedCell //public class CachedCell
{ //{
public ScrollPool Pool { get; } // public ScrollPool Pool { get; }
public RectTransform Rect { get; internal set; } // public RectTransform Rect { get; internal set; }
public ICell Cell { get; } // public ICell Cell { get; }
public CachedCell(ScrollPool pool, RectTransform rect, ICell cell) // public CachedCell(ScrollPool pool, RectTransform rect, ICell cell)
{ // {
this.Pool = pool; // this.Pool = pool;
this.Rect = rect; // this.Rect = rect;
this.Cell = cell; // this.Cell = cell;
} // }
} //}
public ScrollPool(ScrollRect scrollRect) public ScrollPool(ScrollRect scrollRect)
{ {
@ -68,7 +65,7 @@ namespace UnityExplorer.UI.Widgets
private int bottomDataIndex; private int bottomDataIndex;
private int TopDataIndex => bottomDataIndex - CellPool.Count + 1; private int TopDataIndex => bottomDataIndex - CellPool.Count + 1;
private readonly List<CachedCell> CellPool = new List<CachedCell>(); private readonly List<ICell> CellPool = new List<ICell>();
private DataHeightManager HeightCache; private DataHeightManager HeightCache;
@ -77,7 +74,7 @@ namespace UnityExplorer.UI.Widgets
/// <summary> /// <summary>
/// The first and last indices of our CellPool in the transform heirarchy /// The first and last indices of our CellPool in the transform heirarchy
/// </summary> /// </summary>
private int topPoolCellIndex, bottomPoolIndex; private int topPoolIndex, bottomPoolIndex;
private int CurrentDataCount => bottomDataIndex + 1; private int CurrentDataCount => bottomDataIndex + 1;
@ -140,26 +137,31 @@ namespace UnityExplorer.UI.Widgets
private IEnumerator InitCoroutine() private IEnumerator InitCoroutine()
{ {
scrollRect.content.anchoredPosition = Vector2.zero; scrollRect.content.anchoredPosition = Vector2.zero;
yield return null; yield return null;
// set intial bounds
_prevAnchoredPos = Content.anchoredPosition; _prevAnchoredPos = Content.anchoredPosition;
SetRecycleViewBounds(false); SetRecycleViewBounds(false);
float start = Time.realtimeSinceStartup; // create initial cell pool and set cells
CreateCellPool(); CreateCellPool();
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f)); // update slider
SetScrollBounds();
UpdateSliderHandle(); UpdateSliderHandle();
// add onValueChanged listener after setup
scrollRect.onValueChanged.AddListener(OnValueChangedListener); scrollRect.onValueChanged.AddListener(OnValueChangedListener);
} }
private void SetScrollBounds()
{
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f));
}
// Cell pool // Cell pool
private void CreateCellPool() private void CreateCellPool(bool andResetDataIndex = true)
{ {
if (CellPool.Any()) if (CellPool.Any())
{ {
@ -171,7 +173,7 @@ namespace UnityExplorer.UI.Widgets
float currentPoolCoverage = 0f; float currentPoolCoverage = 0f;
float requiredCoverage = scrollRect.viewport.rect.height + ExtraPoolThreshold;// * ExtraPoolCoverageRatio; float requiredCoverage = scrollRect.viewport.rect.height + ExtraPoolThreshold;// * ExtraPoolCoverageRatio;
topPoolCellIndex = 0; topPoolIndex = 0;
bottomPoolIndex = -1; bottomPoolIndex = -1;
// create cells until the Pool area is covered. // create cells until the Pool area is covered.
@ -185,85 +187,65 @@ namespace UnityExplorer.UI.Widgets
rect.gameObject.SetActive(true); rect.gameObject.SetActive(true);
rect.name = $"Cell_{CellPool.Count + 1}"; rect.name = $"Cell_{CellPool.Count + 1}";
var cell = DataSource.CreateCell(rect); var cell = DataSource.CreateCell(rect);
CellPool.Add(new CachedCell(this, rect, cell)); CellPool.Add(cell);
rect.SetParent(scrollRect.content, false); rect.SetParent(scrollRect.content, false);
currentPoolCoverage += rect.rect.height; currentPoolCoverage += rect.rect.height;
} }
bottomDataIndex = CellPool.Count - 1; if (andResetDataIndex)
bottomDataIndex = CellPool.Count - 1;
// after creating pool, set displayed cells. // after creating pool, set displayed cells.
for (int i = 0; i < CellPool.Count; i++) for (int i = 0; i < CellPool.Count; i++)
{ SetCell(CellPool[i], i);
var cell = CellPool[i];
SetCell(cell, i);
}
LayoutRebuilder.ForceRebuildLayoutImmediate(Content); LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
} }
private void SetRecycleViewBounds(bool checkHeightGrow) private void SetRecycleViewBounds(bool checkHeightGrow)
{ {
var extra = ExtraPoolThreshold; RecycleViewBounds = new Vector2(Viewport.MinY() + HalfPoolThreshold, Viewport.MaxY() - HalfPoolThreshold);
extra *= 0.5f;
RecycleViewBounds = new Vector2(Viewport.MinY() + extra, Viewport.MaxY() - extra);
if (checkHeightGrow && _prevViewportHeight < Viewport.rect.height && _prevViewportHeight != 0.0f) if (checkHeightGrow && _prevViewportHeight < Viewport.rect.height && _prevViewportHeight != 0.0f)
RefillCellPool(); ExtendCellPool();
_prevViewportHeight = Viewport.rect.height; _prevViewportHeight = Viewport.rect.height;
} }
private void RefillCellPool() // TODO this is working fine except the content jumps to the top while dragging (then jumps back after).
{ // Not sure why, should be fixable though.
// TODO buggy for some reason, not quite right.
private void ExtendCellPool()
{
var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x); var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x);
var currentCoverage = CellPool.Count * PrototypeHeight; var currentCoverage = CellPool.Count * PrototypeHeight;
if (currentCoverage < requiredCoverage) int cellsRequired = (int)Math.Ceiling((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight);
if (cellsRequired > 0)
{ {
//int cellsRequired = (int)Math.Ceiling((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight); var pos = Content.anchoredPosition.y;
while (currentCoverage <= requiredCoverage) // Disable cells so DataSource can handle its content if need be
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{ {
//bottomPoolIndex++; var curr = enumerator.Current;
ExplorerCore.Log("Adding to end of pool"); DataSource.DisableCell(CellPool[curr.cellIndex], curr.dataIndex);
//Instantiate and add to Pool
RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
rect.gameObject.SetActive(true);
rect.name = $"Cell_{CellPool.Count + 1}";
rect.SetParent(scrollRect.content, false);
currentCoverage += rect.rect.height;
bottomDataIndex++;
} }
CellPool.Clear(); bottomDataIndex += cellsRequired;
int childCount = Content.childCount; // CreateCellPool will destroy existing cells and recreate list.
for (int i = 0; i < childCount; i++) CreateCellPool(false);
{
var rect = Content.GetChild(i).GetComponent<RectTransform>();
var cell = DataSource.CreateCell(rect); //LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
CellPool.Add(new CachedCell(this, rect, cell)); //scrollRect.UpdatePrevData();
ExplorerCore.Log("Assigned cell rect " + i); // set content anchor position back
} Content.anchoredPosition = new Vector2(0, pos);
// reassign cell references LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
topPoolCellIndex = 0; scrollRect.UpdatePrevData();
bottomPoolIndex = CellPool.Count - 1;
// after creating pool, set displayed cells.
for (int i = 0; i < CellPool.Count; i++)
{
var cell = CellPool[i];
SetCell(cell, i);
}
} }
} }
@ -273,7 +255,7 @@ namespace UnityExplorer.UI.Widgets
private IEnumerator<CellInfo> GetPoolEnumerator() private IEnumerator<CellInfo> GetPoolEnumerator()
{ {
int cellIdx = topPoolCellIndex; int cellIdx = topPoolIndex;
int dataIndex = TopDataIndex; int dataIndex = TopDataIndex;
int iterated = 0; int iterated = 0;
while (iterated < CellPool.Count) while (iterated < CellPool.Count)
@ -353,19 +335,20 @@ namespace UnityExplorer.UI.Widgets
LayoutRebuilder.ForceRebuildLayoutImmediate(Content); LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
//SetRecycleViewBounds(false); //SetRecycleViewBounds(false);
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f)); SetScrollBounds();
scrollRect.UpdatePrevData(); scrollRect.UpdatePrevData();
} }
private void SetCell(CachedCell cachedCell, int dataIndex) private void SetCell(ICell cachedCell, int dataIndex)
{ {
cachedCell.Cell.Enable(); cachedCell.Enable();
DataSource.SetCell(cachedCell.Cell, dataIndex); DataSource.SetCell(cachedCell, dataIndex);
// DO NEED THIS! Potentially slightly expensive, but everything breaks if we dont do this. // DO NEED THIS! Potentially slightly expensive, but everything breaks if we dont do this.
LayoutRebuilder.ForceRebuildLayoutImmediate(cachedCell.Rect); LayoutRebuilder.ForceRebuildLayoutImmediate(cachedCell.Rect);
HeightCache.SetIndex(dataIndex, cachedCell.Cell.Enabled ? cachedCell.Rect.rect.height : 0f); if (dataIndex < DataSource.ItemCount)
HeightCache.SetIndex(dataIndex, cachedCell.Rect.rect.height);
} }
// Value change processor // Value change processor
@ -402,18 +385,18 @@ namespace UnityExplorer.UI.Widgets
LayoutRebuilder.ForceRebuildLayoutImmediate(Content); LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
_prevAnchoredPos = scrollRect.content.anchoredPosition; _prevAnchoredPos = scrollRect.content.anchoredPosition;
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f)); SetScrollBounds();
UpdateSliderHandle(); UpdateSliderHandle();
} }
private bool ShouldRecycleTop => GetCellExtent(CellPool[topPoolCellIndex]) >= RecycleViewBounds.x; private bool ShouldRecycleTop => GetCellExtent(CellPool[topPoolIndex].Rect) > RecycleViewBounds.x
//&& CellPool[bottomPoolIndex].Rect.position.y < Viewport.MaxY(); && GetCellExtent(CellPool[bottomPoolIndex].Rect) > RecycleViewBounds.y;
private bool ShouldRecycleBottom => CellPool[bottomPoolIndex].Rect.position.y < RecycleViewBounds.y; private bool ShouldRecycleBottom => CellPool[bottomPoolIndex].Rect.position.y < RecycleViewBounds.y
//&& GetCellExtent(CellPool[topPoolCellIndex]) < Viewport.MinY(); && CellPool[topPoolIndex].Rect.position.y < RecycleViewBounds.x;
private float GetCellExtent(CachedCell cell) => cell.Rect.MaxY() - contentLayout.spacing; private float GetCellExtent(RectTransform cell) => cell.MaxY() - contentLayout.spacing;
private float RecycleTopToBottom() private float RecycleTopToBottom()
{ {
@ -423,7 +406,7 @@ namespace UnityExplorer.UI.Widgets
while (ShouldRecycleTop && CurrentDataCount < DataSource.ItemCount) while (ShouldRecycleTop && CurrentDataCount < DataSource.ItemCount)
{ {
var cell = CellPool[topPoolCellIndex]; var cell = CellPool[topPoolIndex];
//Move top cell to bottom //Move top cell to bottom
cell.Rect.SetAsLastSibling(); cell.Rect.SetAsLastSibling();
@ -439,8 +422,8 @@ namespace UnityExplorer.UI.Widgets
//set new indices //set new indices
bottomDataIndex++; bottomDataIndex++;
bottomPoolIndex = topPoolCellIndex; bottomPoolIndex = topPoolIndex;
topPoolCellIndex = (topPoolCellIndex + 1) % CellPool.Count; topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
} }
return -recycledheight; return -recycledheight;
@ -480,7 +463,7 @@ namespace UnityExplorer.UI.Widgets
} }
//set new indices //set new indices
topPoolCellIndex = bottomPoolIndex; topPoolIndex = bottomPoolIndex;
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count; bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
} }
@ -520,9 +503,10 @@ namespace UnityExplorer.UI.Widgets
val = (float)((decimal)scrollPos / (decimal)(TotalDataHeight - Viewport.rect.height)); val = (float)((decimal)scrollPos / (decimal)(TotalDataHeight - Viewport.rect.height));
} }
bool prev = writingLocked;
WritingLocked = true; WritingLocked = true;
slider.value = val; slider.value = val;
WritingLocked = false; WritingLocked = prev;
} }
} }

View File

@ -19,6 +19,8 @@ namespace UnityExplorer.UI.Widgets
public int _cellIndex; public int _cellIndex;
public GameObject uiRoot; public GameObject uiRoot;
public RectTransform Rect => m_rect;
private readonly RectTransform m_rect;
public Text nameLabel; public Text nameLabel;
public Button nameButton; public Button nameButton;
@ -32,6 +34,7 @@ namespace UnityExplorer.UI.Widgets
{ {
this.tree = tree; this.tree = tree;
this.uiRoot = cellUI; this.uiRoot = cellUI;
m_rect = uiRoot.GetComponent<RectTransform>();
this.nameButton = nameButton; this.nameButton = nameButton;
this.nameLabel = nameButton.GetComponentInChildren<Text>(); this.nameLabel = nameButton.GetComponentInChildren<Text>();
this.expandButton = expandButton; this.expandButton = expandButton;

View File

@ -76,6 +76,8 @@ namespace UnityExplorer.UI.Widgets
Scroller.Initialize(this, prototype); Scroller.Initialize(this, prototype);
} }
public void DisableCell(ICell cell, int index) => cell.Disable();
public ICell CreateCell(RectTransform cellTransform) public ICell CreateCell(RectTransform cellTransform)
{ {
var nameButton = cellTransform.Find("NameButton").GetComponent<Button>(); var nameButton = cellTransform.Find("NameButton").GetComponent<Button>();

View File

@ -273,7 +273,7 @@
<Compile Include="UI\Utility\SignatureHighlighter.cs" /> <Compile Include="UI\Utility\SignatureHighlighter.cs" />
<Compile Include="UI\Utility\ToStringUtility.cs" /> <Compile Include="UI\Utility\ToStringUtility.cs" />
<Compile Include="UI\Widgets\ScrollPool\DataHeightManager.cs" /> <Compile Include="UI\Widgets\ScrollPool\DataHeightManager.cs" />
<Compile Include="UI\Widgets\ScrollPool\DynamicCell.cs" /> <Compile Include="UI\Widgets\ScrollPool\CellViewHolder.cs" />
<Compile Include="UI\Widgets\ScrollPool\ICell.cs" /> <Compile Include="UI\Widgets\ScrollPool\ICell.cs" />
<Compile Include="UI\Widgets\ScrollPool\IPoolDataSource.cs" /> <Compile Include="UI\Widgets\ScrollPool\IPoolDataSource.cs" />
<Compile Include="UI\Widgets\ScrollPool\ScrollPool.cs" /> <Compile Include="UI\Widgets\ScrollPool\ScrollPool.cs" />