2021-04-21 18:44:43 +10:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
2021-04-24 04:02:26 +10:00
|
|
|
|
using UnityEngine;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
namespace UnityExplorer.UI.Widgets
|
|
|
|
|
{
|
|
|
|
|
public class DataViewInfo
|
|
|
|
|
{
|
|
|
|
|
public int dataIndex;
|
|
|
|
|
public float height, startPosition;
|
|
|
|
|
public int normalizedSpread;
|
|
|
|
|
|
2021-04-22 17:53:29 +10:00
|
|
|
|
public static implicit operator float(DataViewInfo it) => it.height;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-22 17:53:29 +10:00
|
|
|
|
public class DataHeightCache
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
|
|
|
|
private ScrollPool ScrollPool { get; }
|
2021-04-24 04:02:26 +10:00
|
|
|
|
private DataHeightCache SisterCache { get; }
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-22 17:53:29 +10:00
|
|
|
|
public DataHeightCache(ScrollPool scrollPool)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
|
|
|
|
ScrollPool = scrollPool;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-22 17:53:29 +10:00
|
|
|
|
public DataHeightCache(ScrollPool scrollPool, DataHeightCache sisterCache) : this(scrollPool)
|
|
|
|
|
{
|
2021-04-24 04:02:26 +10:00
|
|
|
|
this.SisterCache = sisterCache;
|
2021-04-22 17:53:29 +10:00
|
|
|
|
|
2021-04-24 04:02:26 +10:00
|
|
|
|
for (int i = 0; i < scrollPool.DataSource.ItemCount; i++)
|
|
|
|
|
Add(sisterCache[ScrollPool.DataSource.GetRealIndexOfTempIndex(i)]);
|
2021-04-22 17:53:29 +10:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 18:44:43 +10:00
|
|
|
|
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>();
|
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
public DataViewInfo this[int index]
|
|
|
|
|
{
|
|
|
|
|
get => heightCache[index];
|
|
|
|
|
set => SetIndex(index, value);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 18:44:43 +10:00
|
|
|
|
public int Count => heightCache.Count;
|
|
|
|
|
|
|
|
|
|
public float TotalHeight => totalHeight;
|
|
|
|
|
private float totalHeight;
|
|
|
|
|
|
2021-04-24 04:02:26 +10:00
|
|
|
|
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeCell.rect.height);
|
|
|
|
|
private float? m_defaultHeight;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Lookup table for "which data index first appears at this position"<br/>
|
|
|
|
|
/// Index: DefaultHeight * index from top of data<br/>
|
|
|
|
|
/// Value: the first data index at this position<br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly List<int> rangeCache = new List<int>();
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
|
|
|
|
|
private int GetRangeIndexOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
|
|
|
|
|
|
|
|
|
|
/// <summary>Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell.</summary>
|
|
|
|
|
private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the spread of the height, starting from the start position.<br/><br/>
|
|
|
|
|
/// The "spread" begins at the start of the next interval of the DefaultHeight, then increases for
|
|
|
|
|
/// every interval beyond that.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private int GetRangeSpread(float startPosition, float height)
|
2021-04-24 04:02:26 +10:00
|
|
|
|
{
|
2021-04-24 05:23:29 +10:00
|
|
|
|
// get the remainder of the start position divided by min height
|
2021-04-24 04:02:26 +10:00
|
|
|
|
float rem = startPosition % DefaultHeight;
|
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
// if there is a remainder, this means the previous cell started in
|
|
|
|
|
// our first cell and they take priority, so reduce our height by
|
|
|
|
|
// (minHeight - remainder) to account for that. We need to fill that
|
|
|
|
|
// gap and reach the next cell before we take priority.
|
2021-04-24 04:02:26 +10:00
|
|
|
|
if (!Mathf.Approximately(rem, 0f))
|
|
|
|
|
height -= (DefaultHeight - rem);
|
|
|
|
|
|
|
|
|
|
return (int)Math.Ceiling((decimal)height / (decimal)DefaultHeight);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
/// <summary>Append a data index to the cache with the provided height value.</summary>
|
2021-04-21 18:44:43 +10:00
|
|
|
|
public void Add(float value)
|
|
|
|
|
{
|
2021-04-24 05:23:29 +10:00
|
|
|
|
int spread = GetRangeSpread(totalHeight, value);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
heightCache.Add(new DataViewInfo()
|
|
|
|
|
{
|
|
|
|
|
height = value,
|
|
|
|
|
startPosition = TotalHeight,
|
|
|
|
|
normalizedSpread = spread,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
int dataIdx = heightCache.Count - 1;
|
2021-04-24 04:02:26 +10:00
|
|
|
|
for (int i = 0; i < spread; i++)
|
2021-04-24 05:23:29 +10:00
|
|
|
|
rangeCache.Add(dataIdx);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
totalHeight += value;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
/// <summary>Remove the last (highest count) index from the height cache.</summary>
|
2021-04-21 18:44:43 +10:00
|
|
|
|
public void RemoveLast()
|
|
|
|
|
{
|
|
|
|
|
if (!heightCache.Any())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var val = heightCache[heightCache.Count - 1];
|
|
|
|
|
totalHeight -= val;
|
|
|
|
|
heightCache.RemoveAt(heightCache.Count - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 05:23:29 +10:00
|
|
|
|
/// <summary>Get the data index at the specific position of the total height cache.</summary>
|
|
|
|
|
public int GetDataIndexAtPosition(float desiredHeight) => GetDataIndexAtPosition(desiredHeight, out _);
|
|
|
|
|
|
|
|
|
|
/// <summary>Get the data index at the specific position of the total height cache.</summary>
|
|
|
|
|
public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
|
|
|
|
|
{
|
|
|
|
|
cache = null;
|
|
|
|
|
int rangeIndex = GetRangeIndexOfPosition(desiredHeight);
|
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
if (rangeIndex < 0)
|
|
|
|
|
throw new Exception("Range index (" + rangeIndex + ") is below 0");
|
2021-04-24 05:23:29 +10:00
|
|
|
|
|
|
|
|
|
if (rangeCache.Count <= rangeIndex)
|
2021-04-24 16:45:17 +10:00
|
|
|
|
throw new Exception("Range index (" + rangeIndex + ") exceeded rangeCache count (" + rangeCache.Count + ")");
|
2021-04-24 05:23:29 +10:00
|
|
|
|
|
|
|
|
|
int dataIndex = rangeCache[rangeIndex];
|
|
|
|
|
cache = heightCache[dataIndex];
|
|
|
|
|
|
|
|
|
|
return dataIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Set a given data index with the specified value.</summary>
|
2021-04-24 04:02:26 +10:00
|
|
|
|
public void SetIndex(int dataIndex, float height, bool ignoreDataCount = false)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
2021-04-22 17:53:29 +10:00
|
|
|
|
if (!ignoreDataCount)
|
|
|
|
|
{
|
|
|
|
|
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
|
|
|
|
{
|
|
|
|
|
while (heightCache.Count > dataIndex)
|
|
|
|
|
RemoveLast();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
if (dataIndex >= heightCache.Count)
|
|
|
|
|
{
|
|
|
|
|
while (dataIndex > heightCache.Count)
|
|
|
|
|
Add(DefaultHeight);
|
2021-04-24 04:02:26 +10:00
|
|
|
|
Add(height);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cache = heightCache[dataIndex];
|
2021-04-21 23:39:18 +10:00
|
|
|
|
var prevHeight = cache.height;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-24 04:02:26 +10:00
|
|
|
|
var diff = height - prevHeight;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
if (diff != 0.0f)
|
|
|
|
|
{
|
2021-04-24 16:45:17 +10:00
|
|
|
|
// LogWarning("Height for data index " + dataIndex + " changed by " + diff);
|
2021-04-21 23:39:18 +10:00
|
|
|
|
totalHeight += diff;
|
2021-04-24 04:02:26 +10:00
|
|
|
|
cache.height = height;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update our start position using the previous cell (if it exists)
|
|
|
|
|
if (dataIndex > 0)
|
|
|
|
|
{
|
|
|
|
|
var prev = heightCache[dataIndex - 1];
|
|
|
|
|
cache.startPosition = prev.startPosition + prev.height;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
int rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
2021-04-24 05:23:29 +10:00
|
|
|
|
int spread = GetRangeSpread(cache.startPosition, height);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
// check if our range cache is "corrupt" or not. If so we need to do a quick rebuild,
|
|
|
|
|
// so that each cell's start position is correct again.
|
|
|
|
|
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
2021-04-24 16:45:17 +10:00
|
|
|
|
RebuildStartPositions(ignoreDataCount);
|
|
|
|
|
// get these values again after rebuilding
|
|
|
|
|
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
|
|
|
|
spread = GetRangeSpread(cache.startPosition, height);
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
2021-04-24 16:45:17 +10:00
|
|
|
|
|
|
|
|
|
if (spread != cache.normalizedSpread)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
2021-04-24 04:02:26 +10:00
|
|
|
|
// The cell's spread has changed, need to update.
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
int spreadDiff = spread - cache.normalizedSpread;
|
|
|
|
|
cache.normalizedSpread = spread;
|
|
|
|
|
|
2021-04-24 16:45:17 +10:00
|
|
|
|
if (rangeCache[rangeIndex] != dataIndex)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
2021-04-24 16:45:17 +10:00
|
|
|
|
// In some rare cases we may not find our data index at the expected range index.
|
|
|
|
|
// We can make some educated guesses and find the real index pretty quickly.
|
|
|
|
|
int minStart = rangeCache[dataIndex];
|
|
|
|
|
for (int i = minStart; i < rangeCache.Count; i++)
|
2021-04-21 18:44:43 +10:00
|
|
|
|
{
|
2021-04-24 16:45:17 +10:00
|
|
|
|
if (rangeCache[i] == dataIndex)
|
|
|
|
|
{
|
|
|
|
|
rangeIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we somehow reached the end and didn't find the data index...
|
|
|
|
|
if (i == rangeCache.Count - 1)
|
|
|
|
|
{
|
|
|
|
|
// This should never happen. We might be in a rebuild right now so don't
|
|
|
|
|
// rebuild again, we could overflow the stack. Just log it.
|
|
|
|
|
ExplorerCore.LogWarning($"DataHeightCache: Looking for range index of data {dataIndex} but reached the end and didn't find it.");
|
|
|
|
|
ExplorerCore.Log($"startPos: {cache.startPosition}, rangeIndex: {rangeIndex}, total height: {TotalHeight}");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// our data index is further down. add the min difference and try again.
|
|
|
|
|
// the iterator will add 1 on the next loop so account for that.
|
|
|
|
|
// also, add the (spread - 1) of the cell we found at this index to skip it.
|
|
|
|
|
int jmp = dataIndex - rangeCache[i] - 1;
|
|
|
|
|
jmp += heightCache[rangeCache[i]].normalizedSpread - 1;
|
|
|
|
|
i += jmp < 1 ? 0 : jmp;
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
2021-04-24 05:23:29 +10:00
|
|
|
|
}
|
2021-04-21 18:44:43 +10:00
|
|
|
|
|
|
|
|
|
if (spreadDiff > 0)
|
|
|
|
|
{
|
|
|
|
|
// need to insert
|
|
|
|
|
for (int i = 0; i < spreadDiff; i++)
|
2021-04-24 16:45:17 +10:00
|
|
|
|
{
|
|
|
|
|
if (rangeCache[rangeIndex] == dataIndex)
|
|
|
|
|
rangeCache.Insert(rangeIndex, dataIndex);
|
|
|
|
|
else
|
|
|
|
|
ExplorerCore.LogWarning($"DataHeightCache Error increading spread of data {dataIndex}, " +
|
|
|
|
|
$"the value at range {rangeIndex} is {rangeCache[rangeIndex]}!");
|
|
|
|
|
}
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// need to remove
|
|
|
|
|
for (int i = 0; i < -spreadDiff; i++)
|
2021-04-24 16:45:17 +10:00
|
|
|
|
if (rangeCache[rangeIndex] == dataIndex)
|
|
|
|
|
rangeCache.RemoveAt(rangeIndex);
|
|
|
|
|
else
|
|
|
|
|
ExplorerCore.LogWarning($"DataHeightCache Error decreasing spread of data {dataIndex}, " +
|
|
|
|
|
$"the value at range {rangeIndex} is {rangeCache[rangeIndex]}!");
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-22 17:53:29 +10:00
|
|
|
|
|
|
|
|
|
// if sister cache is set, then update it too.
|
2021-04-24 04:02:26 +10:00
|
|
|
|
if (SisterCache != null)
|
2021-04-22 17:53:29 +10:00
|
|
|
|
{
|
|
|
|
|
var realIdx = ScrollPool.DataSource.GetRealIndexOfTempIndex(dataIndex);
|
|
|
|
|
if (realIdx >= 0)
|
2021-04-24 04:02:26 +10:00
|
|
|
|
SisterCache.SetIndex(realIdx, height, true);
|
2021-04-22 17:53:29 +10:00
|
|
|
|
}
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
2021-04-24 16:45:17 +10:00
|
|
|
|
|
|
|
|
|
private void RebuildStartPositions(bool ignoreDataCount)
|
|
|
|
|
{
|
|
|
|
|
//start at 1 because 0's start pos is always 0
|
|
|
|
|
for (int i = 1; i < heightCache.Count; i++)
|
|
|
|
|
SetIndex(i, heightCache[i].height, ignoreDataCount);
|
|
|
|
|
}
|
2021-04-21 18:44:43 +10:00
|
|
|
|
}
|
|
|
|
|
}
|