优化对象池释放逻辑,计数器仅作为对象池复用封装
This commit is contained in:
@@ -269,6 +269,48 @@ namespace XericUI.Core.Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据目标单元格与父级画布的可用空间定位弹窗。
|
||||||
|
/// 默认以左上角对齐单元格左下角;下方空间不足时改用底部锚点显示在单元格上方,
|
||||||
|
/// 右侧空间不足时改为从右侧向左展开。
|
||||||
|
/// </summary>
|
||||||
|
public void AutoSnapAlignedToRect(RectTransform cellRect)
|
||||||
|
{
|
||||||
|
var rt = transform as RectTransform;
|
||||||
|
if (rt == null || cellRect == null) return;
|
||||||
|
|
||||||
|
var parentRT = rt.parent as RectTransform;
|
||||||
|
if (parentRT == null) return;
|
||||||
|
|
||||||
|
Canvas.ForceUpdateCanvases();
|
||||||
|
|
||||||
|
var cellCorners = new Vector3[4];
|
||||||
|
cellRect.GetWorldCorners(cellCorners);
|
||||||
|
for (int i = 0; i < cellCorners.Length; i++)
|
||||||
|
cellCorners[i] = parentRT.InverseTransformPoint(cellCorners[i]);
|
||||||
|
|
||||||
|
var popupWidth = parentRT.InverseTransformVector(
|
||||||
|
rt.TransformVector(Vector3.right * rt.rect.width)).magnitude;
|
||||||
|
var popupHeight = parentRT.InverseTransformVector(
|
||||||
|
rt.TransformVector(Vector3.up * rt.rect.height)).magnitude;
|
||||||
|
var parentRect = parentRT.rect;
|
||||||
|
|
||||||
|
var leftSpace = cellCorners[2].x - parentRect.xMin;
|
||||||
|
var rightSpace = parentRect.xMax - cellCorners[0].x;
|
||||||
|
var placeRight = rightSpace >= popupWidth || rightSpace >= leftSpace;
|
||||||
|
|
||||||
|
var bottomSpace = cellCorners[0].y - parentRect.yMin;
|
||||||
|
var topSpace = parentRect.yMax - cellCorners[1].y;
|
||||||
|
var placeBelow = bottomSpace >= popupHeight || bottomSpace >= topSpace;
|
||||||
|
|
||||||
|
rt.pivot = new Vector2(placeRight ? 0f : 1f, placeBelow ? 1f : 0f);
|
||||||
|
var target = new Vector3(
|
||||||
|
placeRight ? cellCorners[0].x : cellCorners[2].x,
|
||||||
|
placeBelow ? cellCorners[0].y : cellCorners[1].y,
|
||||||
|
0f);
|
||||||
|
rt.position = parentRT.TransformPoint(target);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 显影
|
#region 显影
|
||||||
|
|||||||
@@ -120,10 +120,6 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
ReleaseTracked(pair.Value);
|
ReleaseTracked(pair.Value);
|
||||||
m_Tracked.Clear();
|
m_Tracked.Clear();
|
||||||
m_Rendered.Clear();
|
m_Rendered.Clear();
|
||||||
if (ActivePrefabMap == null) return;
|
|
||||||
var keys = new List<(int, int)>();
|
|
||||||
foreach (var pair in ActivePrefabMap) keys.Add(pair.Key);
|
|
||||||
foreach (var key in keys) ActivePrefabMap.Remove(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReleaseExcept(HashSet<(int, int)> target)
|
private void ReleaseExcept(HashSet<(int, int)> target)
|
||||||
|
|||||||
@@ -19,15 +19,28 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
{
|
{
|
||||||
/// <summary>设置冻结行列数并重建视图</summary>
|
/// <summary>设置冻结行列数并重建视图</summary>
|
||||||
public void SetFreezeCount(int rows, int cols)
|
public void SetFreezeCount(int rows, int cols)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetFreezeCount, rows, cols))) return;
|
||||||
|
SetFreezeCountImmediate(rows, cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetFreezeCountImmediate(int rows, int cols, bool refresh = true)
|
||||||
{
|
{
|
||||||
m_FreezeRowCount = Mathf.Max(0, rows);
|
m_FreezeRowCount = Mathf.Max(0, rows);
|
||||||
m_FreezeColCount = Mathf.Max(0, cols);
|
m_FreezeColCount = Mathf.Max(0, cols);
|
||||||
CreateViewportRenderer();
|
CreateViewportRenderer();
|
||||||
MarkDirty(TableDirtyType.LayoutChanged);
|
MarkDirty(TableDirtyType.LayoutChanged);
|
||||||
RefreshView();
|
if (refresh && !IsCheckingUpdate)
|
||||||
|
RefreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCellData(int row, int col, XTableCellData data)
|
public void SetCellData(int row, int col, XTableCellData data)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellData, row, col, cellData: data))) return;
|
||||||
|
SetCellDataImmediate(row, col, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetCellDataImmediate(int row, int col, XTableCellData data)
|
||||||
{
|
{
|
||||||
TableData.SetCell(row, col, data);
|
TableData.SetCell(row, col, data);
|
||||||
MarkDirty(TableDirtyType.DataChanged);
|
MarkDirty(TableDirtyType.DataChanged);
|
||||||
@@ -35,6 +48,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置文本内容(ContentType 自动设为 Text)</summary>
|
/// <summary>设置文本内容(ContentType 自动设为 Text)</summary>
|
||||||
public XTableCellData SetCellText(int row, int col, string text)
|
public XTableCellData SetCellText(int row, int col, string text)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellText, row, col, stringValue1: text))) return null;
|
||||||
|
return SetCellTextImmediate(row, col, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XTableCellData SetCellTextImmediate(int row, int col, string text)
|
||||||
{
|
{
|
||||||
var data = TableData.GetOrCreateCell(row, col);
|
var data = TableData.GetOrCreateCell(row, col);
|
||||||
data.Text = text;
|
data.Text = text;
|
||||||
@@ -45,6 +64,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置图片纹理(ContentType 自动设为 Image)</summary>
|
/// <summary>设置图片纹理(ContentType 自动设为 Image)</summary>
|
||||||
public XTableCellData SetCellImage(int row, int col, Texture2D image)
|
public XTableCellData SetCellImage(int row, int col, Texture2D image)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellImage, row, col, texture: image))) return null;
|
||||||
|
return SetCellImageImmediate(row, col, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XTableCellData SetCellImageImmediate(int row, int col, Texture2D image)
|
||||||
{
|
{
|
||||||
var data = TableData.GetOrCreateCell(row, col);
|
var data = TableData.GetOrCreateCell(row, col);
|
||||||
data.Image = image;
|
data.Image = image;
|
||||||
@@ -56,6 +81,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// <summary>设置预制体(ContentType 自动设为 Prefab)</summary>
|
/// <summary>设置预制体(ContentType 自动设为 Prefab)</summary>
|
||||||
/// <remarks>注意是设置预制体而不是实例,设置后订阅单元格预制体生成事件 OnPrefabAcquire 处理获取事件</remarks>
|
/// <remarks>注意是设置预制体而不是实例,设置后订阅单元格预制体生成事件 OnPrefabAcquire 处理获取事件</remarks>
|
||||||
public XTableCellData SetCellPrefab(int row, int col, GameObject prefab)
|
public XTableCellData SetCellPrefab(int row, int col, GameObject prefab)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellPrefab, row, col, prefab: prefab))) return null;
|
||||||
|
return SetCellPrefabImmediate(row, col, prefab);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XTableCellData SetCellPrefabImmediate(int row, int col, GameObject prefab)
|
||||||
{
|
{
|
||||||
var data = TableData.GetOrCreateCell(row, col);
|
var data = TableData.GetOrCreateCell(row, col);
|
||||||
data.Prefab = prefab;
|
data.Prefab = prefab;
|
||||||
@@ -66,6 +97,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置单元格或合并矩形的样式,并覆写其四条外边范围。</summary>
|
/// <summary>设置单元格或合并矩形的样式,并覆写其四条外边范围。</summary>
|
||||||
public XTableCellData SetCellStyle(int row, int col, string styleNamespace, string stylePath)
|
public XTableCellData SetCellStyle(int row, int col, string styleNamespace, string stylePath)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellStyle, row, col, stringValue1: styleNamespace, stringValue2: stylePath))) return null;
|
||||||
|
return SetCellStyleImmediate(row, col, styleNamespace, stylePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XTableCellData SetCellStyleImmediate(int row, int col, string styleNamespace, string stylePath)
|
||||||
{
|
{
|
||||||
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
||||||
XTableCellData result = null;
|
XTableCellData result = null;
|
||||||
@@ -86,6 +123,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置单元格的样式命名空间(兼容旧版,等同于 StyleNamespace 字段)</summary>
|
/// <summary>设置单元格的样式命名空间(兼容旧版,等同于 StyleNamespace 字段)</summary>
|
||||||
public void SetCellStyleNamespace(int row, int col, string styleNamespace)
|
public void SetCellStyleNamespace(int row, int col, string styleNamespace)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetCellStyleNamespace, row, col, stringValue1: styleNamespace))) return;
|
||||||
|
SetCellStyleNamespaceImmediate(row, col, styleNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetCellStyleNamespaceImmediate(int row, int col, string styleNamespace)
|
||||||
{
|
{
|
||||||
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
||||||
for (int r = rowStart; r < rowEnd; r++)
|
for (int r = rowStart; r < rowEnd; r++)
|
||||||
@@ -108,6 +151,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>清除文本内容</summary>
|
/// <summary>清除文本内容</summary>
|
||||||
public void ClearCellText(int row, int col)
|
public void ClearCellText(int row, int col)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.ClearCellText, row, col))) return;
|
||||||
|
ClearCellTextImmediate(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCellTextImmediate(int row, int col)
|
||||||
{
|
{
|
||||||
var data = TableData.GetCell(row, col);
|
var data = TableData.GetCell(row, col);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
@@ -119,6 +168,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>清除图片纹理</summary>
|
/// <summary>清除图片纹理</summary>
|
||||||
public void ClearCellImage(int row, int col)
|
public void ClearCellImage(int row, int col)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.ClearCellImage, row, col))) return;
|
||||||
|
ClearCellImageImmediate(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCellImageImmediate(int row, int col)
|
||||||
{
|
{
|
||||||
var data = TableData.GetCell(row, col);
|
var data = TableData.GetCell(row, col);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
@@ -128,6 +183,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>清除预制体及委托</summary>
|
/// <summary>清除预制体及委托</summary>
|
||||||
public void ClearCellPrefab(int row, int col)
|
public void ClearCellPrefab(int row, int col)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.ClearCellPrefab, row, col))) return;
|
||||||
|
ClearCellPrefabImmediate(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCellPrefabImmediate(int row, int col)
|
||||||
{
|
{
|
||||||
var data = TableData.GetCell(row, col);
|
var data = TableData.GetCell(row, col);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
@@ -139,6 +200,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>清除单元格样式覆盖</summary>
|
/// <summary>清除单元格样式覆盖</summary>
|
||||||
public void ClearCellStyle(int row, int col)
|
public void ClearCellStyle(int row, int col)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.ClearCellStyle, row, col))) return;
|
||||||
|
ClearCellStyleImmediate(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCellStyleImmediate(int row, int col)
|
||||||
{
|
{
|
||||||
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
ResolveStyleRect(row, col, out int rowStart, out int rowEnd, out int colStart, out int colEnd);
|
||||||
for (int r = rowStart; r < rowEnd; r++)
|
for (int r = rowStart; r < rowEnd; r++)
|
||||||
@@ -154,7 +221,10 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
public XTableCellData GetCellData(int row, int col)
|
public XTableCellData GetCellData(int row, int col)
|
||||||
=> TableData.GetOrCreateCell(row, col);
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(GetCellData));
|
||||||
|
return TableData.GetOrCreateCell(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>获取单元格的样式命名空间(含回退逻辑)</summary>
|
/// <summary>获取单元格的样式命名空间(含回退逻辑)</summary>
|
||||||
public string GetCellStyleNamespace(int row, int col)
|
public string GetCellStyleNamespace(int row, int col)
|
||||||
@@ -164,12 +234,24 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void MergeCells(int startRow, int startCol, int rowSpan, int colSpan)
|
public void MergeCells(int startRow, int startCol, int rowSpan, int colSpan)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.MergeCells, startRow, startCol, rowSpan, colSpan))) return;
|
||||||
|
MergeCellsImmediate(startRow, startCol, rowSpan, colSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MergeCellsImmediate(int startRow, int startCol, int rowSpan, int colSpan)
|
||||||
{
|
{
|
||||||
TableData.MergeCells(startRow, startCol, rowSpan, colSpan);
|
TableData.MergeCells(startRow, startCol, rowSpan, colSpan);
|
||||||
MarkDirty(TableDirtyType.DataChanged);
|
MarkDirty(TableDirtyType.DataChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnmergeCells(int startRow, int startCol)
|
public void UnmergeCells(int startRow, int startCol)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.UnmergeCells, startRow, startCol))) return;
|
||||||
|
UnmergeCellsImmediate(startRow, startCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnmergeCellsImmediate(int startRow, int startCol)
|
||||||
{
|
{
|
||||||
TableData.UnmergeCells(startRow, startCol);
|
TableData.UnmergeCells(startRow, startCol);
|
||||||
MarkDirty(TableDirtyType.DataChanged);
|
MarkDirty(TableDirtyType.DataChanged);
|
||||||
@@ -184,6 +266,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>在末尾追加一行,默认行高来自 Config</summary>
|
/// <summary>在末尾追加一行,默认行高来自 Config</summary>
|
||||||
public void AddRow(float height = -1f)
|
public void AddRow(float height = -1f)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.AddRow, floatValue: height))) return;
|
||||||
|
AddRowImmediate(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRowImmediate(float height)
|
||||||
{
|
{
|
||||||
if (height <= 0) height = Config.DefaultRowHeight;
|
if (height <= 0) height = Config.DefaultRowHeight;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -194,7 +282,6 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
System.Array.Copy(m_RowHeights, newArr, m_RowHeights.Length);
|
System.Array.Copy(m_RowHeights, newArr, m_RowHeights.Length);
|
||||||
newArr[newLen - 1] = height;
|
newArr[newLen - 1] = height;
|
||||||
m_RowHeights = newArr;
|
m_RowHeights = newArr;
|
||||||
RecordStructuralChange(false);
|
|
||||||
MarkDirty(TableDirtyType.LayoutChanged);
|
MarkDirty(TableDirtyType.LayoutChanged);
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEditor.EditorUtility.SetDirty(this);
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
@@ -203,6 +290,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>在末尾追加一列,默认列宽来自 Config</summary>
|
/// <summary>在末尾追加一列,默认列宽来自 Config</summary>
|
||||||
public void AddColumn(float width = -1f)
|
public void AddColumn(float width = -1f)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.AddColumn, floatValue: width))) return;
|
||||||
|
AddColumnImmediate(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddColumnImmediate(float width)
|
||||||
{
|
{
|
||||||
if (width <= 0) width = Config.DefaultColumnWidth;
|
if (width <= 0) width = Config.DefaultColumnWidth;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@@ -213,7 +306,6 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
System.Array.Copy(m_ColWidths, newArr, m_ColWidths.Length);
|
System.Array.Copy(m_ColWidths, newArr, m_ColWidths.Length);
|
||||||
newArr[newLen - 1] = width;
|
newArr[newLen - 1] = width;
|
||||||
m_ColWidths = newArr;
|
m_ColWidths = newArr;
|
||||||
RecordStructuralChange(false);
|
|
||||||
MarkDirty(TableDirtyType.LayoutChanged);
|
MarkDirty(TableDirtyType.LayoutChanged);
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEditor.EditorUtility.SetDirty(this);
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
@@ -222,6 +314,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置指定行的高度</summary>
|
/// <summary>设置指定行的高度</summary>
|
||||||
public void SetRowHeight(int row, float height)
|
public void SetRowHeight(int row, float height)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetRowHeight, row, floatValue: height))) return;
|
||||||
|
SetRowHeightImmediate(row, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRowHeightImmediate(int row, float height)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= m_RowHeights.Length) return;
|
if (row < 0 || row >= m_RowHeights.Length) return;
|
||||||
if (height <= 0) return;
|
if (height <= 0) return;
|
||||||
@@ -237,6 +335,12 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>设置指定列的宽度</summary>
|
/// <summary>设置指定列的宽度</summary>
|
||||||
public void SetColWidth(int col, float width)
|
public void SetColWidth(int col, float width)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.SetColWidth, col: col, floatValue: width))) return;
|
||||||
|
SetColWidthImmediate(col, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetColWidthImmediate(int col, float width)
|
||||||
{
|
{
|
||||||
if (col < 0 || col >= m_ColWidths.Length) return;
|
if (col < 0 || col >= m_ColWidths.Length) return;
|
||||||
if (width <= 0) return;
|
if (width <= 0) return;
|
||||||
@@ -252,15 +356,18 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>删除指定行,单元格数据上移</summary>
|
/// <summary>删除指定行,单元格数据上移</summary>
|
||||||
public void RemoveRow(int row)
|
public void RemoveRow(int row)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.RemoveRow, row))) return;
|
||||||
|
RemoveRowImmediate(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveRowImmediate(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= m_RowHeights.Length) return;
|
if (row < 0 || row >= m_RowHeights.Length) return;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEditor.Undo.RecordObject(this, "Remove Table Row");
|
UnityEditor.Undo.RecordObject(this, "Remove Table Row");
|
||||||
#endif
|
#endif
|
||||||
int colCount = m_ColWidths.Length;
|
int colCount = m_ColWidths.Length;
|
||||||
CaptureActivePrefabReleaseCallbacks();
|
|
||||||
RecordStructuralChange(true);
|
|
||||||
|
|
||||||
// 先解除涉及该行的合并
|
// 先解除涉及该行的合并
|
||||||
for (int c = 0; c < colCount; c++)
|
for (int c = 0; c < colCount; c++)
|
||||||
TableData.UnmergeCells(row, c);
|
TableData.UnmergeCells(row, c);
|
||||||
@@ -289,6 +396,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
for (int i = row; i < newLen; i++)
|
for (int i = row; i < newLen; i++)
|
||||||
newArr[i] = m_RowHeights[i + 1];
|
newArr[i] = m_RowHeights[i + 1];
|
||||||
m_RowHeights = newArr;
|
m_RowHeights = newArr;
|
||||||
|
TableData.ClearAllBorderCache();
|
||||||
|
|
||||||
// 裁剪选区到新尺寸
|
// 裁剪选区到新尺寸
|
||||||
ClampSelectionToBounds();
|
ClampSelectionToBounds();
|
||||||
@@ -301,15 +409,18 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>删除指定列,单元格数据左移</summary>
|
/// <summary>删除指定列,单元格数据左移</summary>
|
||||||
public void RemoveColumn(int col)
|
public void RemoveColumn(int col)
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.RemoveColumn, col: col))) return;
|
||||||
|
RemoveColumnImmediate(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveColumnImmediate(int col)
|
||||||
{
|
{
|
||||||
if (col < 0 || col >= m_ColWidths.Length) return;
|
if (col < 0 || col >= m_ColWidths.Length) return;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEditor.Undo.RecordObject(this, "Remove Table Column");
|
UnityEditor.Undo.RecordObject(this, "Remove Table Column");
|
||||||
#endif
|
#endif
|
||||||
int rowCount = m_RowHeights.Length;
|
int rowCount = m_RowHeights.Length;
|
||||||
CaptureActivePrefabReleaseCallbacks();
|
|
||||||
RecordStructuralChange(true);
|
|
||||||
|
|
||||||
// 先解除涉及该列的合并
|
// 先解除涉及该列的合并
|
||||||
for (int r = 0; r < rowCount; r++)
|
for (int r = 0; r < rowCount; r++)
|
||||||
TableData.UnmergeCells(r, col);
|
TableData.UnmergeCells(r, col);
|
||||||
@@ -338,6 +449,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
for (int i = col; i < newLen; i++)
|
for (int i = col; i < newLen; i++)
|
||||||
newArr[i] = m_ColWidths[i + 1];
|
newArr[i] = m_ColWidths[i + 1];
|
||||||
m_ColWidths = newArr;
|
m_ColWidths = newArr;
|
||||||
|
TableData.ClearAllBorderCache();
|
||||||
|
|
||||||
// 裁剪选区到新尺寸
|
// 裁剪选区到新尺寸
|
||||||
ClampSelectionToBounds();
|
ClampSelectionToBounds();
|
||||||
@@ -432,6 +544,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
[ContextMenu("Xeric UI/完全重建表格")]
|
[ContextMenu("Xeric UI/完全重建表格")]
|
||||||
public void FullRebuild()
|
public void FullRebuild()
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(FullRebuild));
|
||||||
m_IsRebuilding = true;
|
m_IsRebuilding = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -470,6 +583,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// <summary>重新生成表格但保留已有文本数据——先保存,重建后再回填</summary>
|
/// <summary>重新生成表格但保留已有文本数据——先保存,重建后再回填</summary>
|
||||||
public void FullRebuildPreservingData()
|
public void FullRebuildPreservingData()
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(FullRebuildPreservingData));
|
||||||
m_IsRebuilding = true;
|
m_IsRebuilding = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -540,6 +654,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearData()
|
public void ClearData()
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(ClearData));
|
||||||
if (m_TableData != null)
|
if (m_TableData != null)
|
||||||
{
|
{
|
||||||
// 清理预制体:先调用 OnPrefabRelease 再清理映射
|
// 清理预制体:先调用 OnPrefabRelease 再清理映射
|
||||||
@@ -616,10 +731,17 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
/// <summary>仅强制刷新样式,不重建数据结构</summary>
|
/// <summary>仅强制刷新样式,不重建数据结构</summary>
|
||||||
public void ForceStyleRefresh()
|
public void ForceStyleRefresh()
|
||||||
|
{
|
||||||
|
if (TryEnqueueUpdateCommand(new UpdateCommand(UpdateCommandType.ForceStyleRefresh))) return;
|
||||||
|
ForceStyleRefreshImmediate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ForceStyleRefreshImmediate()
|
||||||
{
|
{
|
||||||
StyleManager.ForceRefresh();
|
StyleManager.ForceRefresh();
|
||||||
MarkDirty(TableDirtyType.StyleChanged);
|
MarkDirty(TableDirtyType.StyleChanged);
|
||||||
RefreshView();
|
if (!IsCheckingUpdate)
|
||||||
|
RefreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>构建剪贴板数据对象(尺寸 + 单元格文本 + 合并信息)</summary>
|
/// <summary>构建剪贴板数据对象(尺寸 + 单元格文本 + 合并信息)</summary>
|
||||||
@@ -631,10 +753,8 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// <summary>将剪贴板数据应用到表格——覆盖尺寸、命名空间、单元格文本、合并信息并刷新</summary>
|
/// <summary>将剪贴板数据应用到表格——覆盖尺寸、命名空间、单元格文本、合并信息并刷新</summary>
|
||||||
public void ApplyClipboardData(XTableClipboardData data)
|
public void ApplyClipboardData(XTableClipboardData data)
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(ApplyClipboardData));
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
CaptureActivePrefabReleaseCallbacks();
|
|
||||||
RecordStructuralChange(true);
|
|
||||||
|
|
||||||
// 确保数据层存在
|
// 确保数据层存在
|
||||||
if (m_TableData == null)
|
if (m_TableData == null)
|
||||||
m_TableData = new XTableData();
|
m_TableData = new XTableData();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// <param name="sheetIndex">工作表索引(0-based),默认 0 即第一个工作表</param>
|
/// <param name="sheetIndex">工作表索引(0-based),默认 0 即第一个工作表</param>
|
||||||
public void LoadFromExcelFile(string filePath, int sheetIndex = 0)
|
public void LoadFromExcelFile(string filePath, int sheetIndex = 0)
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(LoadFromExcelFile));
|
||||||
if (!TryOpenPackage(filePath, out ExcelPackage package)) return;
|
if (!TryOpenPackage(filePath, out ExcelPackage package)) return;
|
||||||
using (package)
|
using (package)
|
||||||
{
|
{
|
||||||
@@ -46,6 +47,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
/// <param name="sheetName">工作表名称</param>
|
/// <param name="sheetName">工作表名称</param>
|
||||||
public void LoadFromExcelFile(string filePath, string sheetName)
|
public void LoadFromExcelFile(string filePath, string sheetName)
|
||||||
{
|
{
|
||||||
|
ThrowIfCheckingUpdate(nameof(LoadFromExcelFile));
|
||||||
if (string.IsNullOrEmpty(sheetName))
|
if (string.IsNullOrEmpty(sheetName))
|
||||||
{
|
{
|
||||||
Debug.LogWarning("[XTable] LoadFromExcelFile: 工作表名称为空");
|
Debug.LogWarning("[XTable] LoadFromExcelFile: 工作表名称为空");
|
||||||
@@ -262,8 +264,6 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 标记脏并刷新
|
// 标记脏并刷新
|
||||||
CaptureActivePrefabReleaseCallbacks();
|
|
||||||
RecordStructuralChange(true);
|
|
||||||
RecalculateTotalSize();
|
RecalculateTotalSize();
|
||||||
RequestRefresh(TableDirtyType.DataChanged | TableDirtyType.LayoutChanged, true);
|
RequestRefresh(TableDirtyType.DataChanged | TableDirtyType.LayoutChanged, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,39 +225,22 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
#region 完整刷新
|
#region 完整刷新
|
||||||
|
|
||||||
private void CommitStructuralRenderCleanup(bool invalidatePrefabBindings,
|
private void ReleaseRenderedBindings(Dictionary<GameObject, System.Action<GameObject>> prefabReleaseCallbacks = null)
|
||||||
Dictionary<GameObject, System.Action<GameObject>> prefabReleaseCallbacks)
|
|
||||||
{
|
{
|
||||||
ReleaseAllTrackedCells();
|
ReleaseAllTrackedCells();
|
||||||
ReleaseFrozenCells();
|
ReleaseFrozenCells();
|
||||||
m_LastRenderedCells.Clear();
|
m_LastRenderedCells.Clear();
|
||||||
|
m_PrefabPool?.ReturnAll(instance =>
|
||||||
if (m_ActivePrefabMap != null)
|
|
||||||
{
|
{
|
||||||
foreach (var pair in m_ActivePrefabMap)
|
if (prefabReleaseCallbacks != null && prefabReleaseCallbacks.TryGetValue(instance, out var callback))
|
||||||
{
|
callback(instance);
|
||||||
if (prefabReleaseCallbacks != null && prefabReleaseCallbacks.ContainsKey(pair.Value)) continue;
|
});
|
||||||
var data = TableData.GetCell(pair.Key.Item1, pair.Key.Item2);
|
m_ActivePrefabMap?.Clear();
|
||||||
if (data?.OnPrefabRelease != null && prefabReleaseCallbacks != null
|
|
||||||
&& !prefabReleaseCallbacks.ContainsKey(pair.Value))
|
|
||||||
prefabReleaseCallbacks.Add(pair.Value, data.OnPrefabRelease);
|
|
||||||
}
|
|
||||||
m_ActivePrefabMap.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalidatePrefabBindings)
|
|
||||||
m_PrefabPool?.ReleaseAll(instance =>
|
|
||||||
{
|
|
||||||
if (prefabReleaseCallbacks != null && prefabReleaseCallbacks.TryGetValue(instance, out var callback))
|
|
||||||
callback(instance);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_QuadtreeDirty = true;
|
|
||||||
m_StyleNamespaceDirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshView()
|
public void RefreshView(bool releaseBindings = true)
|
||||||
{
|
{
|
||||||
|
TableDirtyType refreshedFlags = m_DirtyFlag.Flags;
|
||||||
if (m_Assembler == null)
|
if (m_Assembler == null)
|
||||||
{
|
{
|
||||||
if (rectTransform != null)
|
if (rectTransform != null)
|
||||||
@@ -288,19 +271,8 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
UpdateBackground();
|
UpdateBackground();
|
||||||
|
|
||||||
// 归还所有追踪对象 + 预制体
|
if (releaseBindings)
|
||||||
if (m_ActivePrefabMap != null)
|
ReleaseRenderedBindings(CaptureActivePrefabReleaseCallbacks());
|
||||||
{
|
|
||||||
foreach (var kv in m_ActivePrefabMap)
|
|
||||||
{
|
|
||||||
var cellData = TableData.GetCell(kv.Key.Item1, kv.Key.Item2);
|
|
||||||
cellData?.OnPrefabRelease?.Invoke(kv.Value);
|
|
||||||
}
|
|
||||||
m_ActivePrefabMap.Clear();
|
|
||||||
}
|
|
||||||
m_PrefabPool?.ReturnAll();
|
|
||||||
ReleaseAllTrackedCells();
|
|
||||||
ReleaseFrozenCells();
|
|
||||||
|
|
||||||
// 清理孤立子对象
|
// 清理孤立子对象
|
||||||
CleanOrphanChildren();
|
CleanOrphanChildren();
|
||||||
@@ -320,7 +292,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
RenderFrozenCells(true);
|
RenderFrozenCells(true);
|
||||||
RebuildBorders();
|
RebuildBorders();
|
||||||
|
|
||||||
m_DirtyFlag.Clear();
|
m_DirtyFlag.ClearFlags(refreshedFlags);
|
||||||
OnTableRefreshed?.Invoke();
|
OnTableRefreshed?.Invoke();
|
||||||
SetChildrenVisible(m_ChildrenVisible);
|
SetChildrenVisible(m_ChildrenVisible);
|
||||||
}
|
}
|
||||||
@@ -380,7 +352,9 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
|
|
||||||
foreach (var key in m_ReusableCellList)
|
foreach (var key in m_ReusableCellList)
|
||||||
{
|
{
|
||||||
// 不调用 OnPrefabRelease 和 Release——预制体保留在槽位中
|
GameObject instance = m_ActivePrefabMap[key];
|
||||||
|
var data = TableData.GetCell(key.Item1, key.Item2);
|
||||||
|
m_PrefabPool?.Release(instance, data?.OnPrefabRelease);
|
||||||
m_ActivePrefabMap.Remove(key);
|
m_ActivePrefabMap.Remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,7 +372,7 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
// 5. 交换:将 newVisible 作为下一帧的比较基准
|
// 5. 交换:将 newVisible 作为下一帧的比较基准
|
||||||
(m_LastRenderedCells, newVisible) = (newVisible, m_LastRenderedCells);
|
(m_LastRenderedCells, newVisible) = (newVisible, m_LastRenderedCells);
|
||||||
|
|
||||||
m_DirtyFlag.ClearFlags(TableDirtyType.ScrollChanged | TableDirtyType.SelectionChanged);
|
m_DirtyFlag.ClearFlags(refreshedFlags & (TableDirtyType.ScrollChanged | TableDirtyType.SelectionChanged));
|
||||||
OnTableRefreshed?.Invoke();
|
OnTableRefreshed?.Invoke();
|
||||||
SetChildrenVisible(m_ChildrenVisible);
|
SetChildrenVisible(m_ChildrenVisible);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,84 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using XericUI.XTable.Core;
|
||||||
|
|
||||||
namespace XericUI.XTable.Rendering.Component
|
namespace XericUI.XTable.Rendering.Component
|
||||||
{
|
{
|
||||||
public partial class XericUIActionTable
|
public partial class XericUIActionTable
|
||||||
{
|
{
|
||||||
|
private enum UpdateCommandType
|
||||||
|
{
|
||||||
|
SetFreezeCount,
|
||||||
|
SetCellData,
|
||||||
|
SetCellText,
|
||||||
|
SetCellImage,
|
||||||
|
SetCellPrefab,
|
||||||
|
ClearCellText,
|
||||||
|
ClearCellImage,
|
||||||
|
ClearCellPrefab,
|
||||||
|
SetCellStyle,
|
||||||
|
SetCellStyleNamespace,
|
||||||
|
ClearCellStyle,
|
||||||
|
MergeCells,
|
||||||
|
UnmergeCells,
|
||||||
|
AddRow,
|
||||||
|
AddColumn,
|
||||||
|
RemoveRow,
|
||||||
|
RemoveColumn,
|
||||||
|
SetRowHeight,
|
||||||
|
SetColWidth,
|
||||||
|
ForceStyleRefresh
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct UpdateCommand
|
||||||
|
{
|
||||||
|
public readonly UpdateCommandType Type;
|
||||||
|
public readonly int Row;
|
||||||
|
public readonly int Col;
|
||||||
|
public readonly int IntValue1;
|
||||||
|
public readonly int IntValue2;
|
||||||
|
public readonly float FloatValue;
|
||||||
|
public readonly string StringValue1;
|
||||||
|
public readonly string StringValue2;
|
||||||
|
public readonly XTableCellData CellData;
|
||||||
|
public readonly Texture2D Texture;
|
||||||
|
public readonly GameObject Prefab;
|
||||||
|
|
||||||
|
public UpdateCommand(UpdateCommandType type, int row = 0, int col = 0,
|
||||||
|
int intValue1 = 0, int intValue2 = 0, float floatValue = 0f,
|
||||||
|
string stringValue1 = null, string stringValue2 = null,
|
||||||
|
XTableCellData cellData = null, Texture2D texture = null, GameObject prefab = null)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Row = row;
|
||||||
|
Col = col;
|
||||||
|
IntValue1 = intValue1;
|
||||||
|
IntValue2 = intValue2;
|
||||||
|
FloatValue = floatValue;
|
||||||
|
StringValue1 = stringValue1;
|
||||||
|
StringValue2 = stringValue2;
|
||||||
|
CellData = cellData;
|
||||||
|
Texture = texture;
|
||||||
|
Prefab = prefab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class UpdateContext
|
private sealed class UpdateContext
|
||||||
{
|
{
|
||||||
public TableDirtyType DirtyFlags;
|
public TableDirtyType DirtyFlags;
|
||||||
public bool HasStructuralChange;
|
public readonly List<UpdateCommand> Commands = new();
|
||||||
public bool InvalidatePrefabBindings;
|
|
||||||
public bool RequiresImmediateRefresh;
|
|
||||||
public readonly Dictionary<GameObject, Action<GameObject>> PrefabReleaseCallbacks = new();
|
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
DirtyFlags = TableDirtyType.None;
|
DirtyFlags = TableDirtyType.None;
|
||||||
HasStructuralChange = false;
|
Commands.Clear();
|
||||||
InvalidatePrefabBindings = false;
|
|
||||||
RequiresImmediateRefresh = false;
|
|
||||||
PrefabReleaseCallbacks.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MergeFrom(UpdateContext child)
|
public void MergeFrom(UpdateContext child)
|
||||||
{
|
{
|
||||||
DirtyFlags |= child.DirtyFlags;
|
DirtyFlags |= child.DirtyFlags;
|
||||||
HasStructuralChange |= child.HasStructuralChange;
|
Commands.AddRange(child.Commands);
|
||||||
InvalidatePrefabBindings |= child.InvalidatePrefabBindings;
|
|
||||||
RequiresImmediateRefresh |= child.RequiresImmediateRefresh;
|
|
||||||
foreach (var pair in child.PrefabReleaseCallbacks)
|
|
||||||
if (!PrefabReleaseCallbacks.ContainsKey(pair.Key))
|
|
||||||
PrefabReleaseCallbacks.Add(pair.Key, pair.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,25 +98,22 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
{
|
{
|
||||||
if (m_Disposed) return;
|
if (m_Disposed) return;
|
||||||
m_Disposed = true;
|
m_Disposed = true;
|
||||||
if (m_Table != null)
|
m_Table?.EndCheckUpdate(m_Token);
|
||||||
m_Table.EndCheckUpdate(m_Token);
|
|
||||||
m_Table = null;
|
m_Table = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NonSerialized] private readonly Stack<UpdateContext> m_UpdateContextStack = new();
|
[NonSerialized] private readonly Stack<UpdateContext> m_UpdateContextStack = new();
|
||||||
[NonSerialized] private readonly Stack<UpdateContext> m_UpdateContextPool = new();
|
[NonSerialized] private readonly Stack<UpdateContext> m_UpdateContextPool = new();
|
||||||
[NonSerialized] private long m_NextUpdateToken;
|
|
||||||
[NonSerialized] private readonly Stack<long> m_UpdateTokenStack = new();
|
[NonSerialized] private readonly Stack<long> m_UpdateTokenStack = new();
|
||||||
|
[NonSerialized] private long m_NextUpdateToken;
|
||||||
|
|
||||||
private bool IsCheckingUpdate => m_UpdateContextStack.Count > 0;
|
private bool IsCheckingUpdate => m_UpdateContextStack.Count > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>开始一个可嵌套的命令缓冲作用域。可变 API 在最外层 Dispose 时按调用顺序执行。</summary>
|
||||||
/// 开始一个可嵌套的批量更新作用域。作用域内的数据修改立即生效,渲染与结构资源整理延迟到最外层 Dispose。
|
|
||||||
/// </summary>
|
|
||||||
public UpdateScope CheckUpdate()
|
public UpdateScope CheckUpdate()
|
||||||
{
|
{
|
||||||
var context = m_UpdateContextPool.Count > 0 ? m_UpdateContextPool.Pop() : new UpdateContext();
|
UpdateContext context = m_UpdateContextPool.Count > 0 ? m_UpdateContextPool.Pop() : new UpdateContext();
|
||||||
context.Reset();
|
context.Reset();
|
||||||
m_UpdateContextStack.Push(context);
|
m_UpdateContextStack.Push(context);
|
||||||
long token = ++m_NextUpdateToken;
|
long token = ++m_NextUpdateToken;
|
||||||
@@ -77,6 +121,13 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
return new UpdateScope(this, token);
|
return new UpdateScope(this, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryEnqueueUpdateCommand(UpdateCommand command)
|
||||||
|
{
|
||||||
|
if (!IsCheckingUpdate) return false;
|
||||||
|
m_UpdateContextStack.Peek().Commands.Add(command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void EndCheckUpdate(long token)
|
private void EndCheckUpdate(long token)
|
||||||
{
|
{
|
||||||
if (m_UpdateTokenStack.Count == 0 || m_UpdateTokenStack.Peek() != token)
|
if (m_UpdateTokenStack.Count == 0 || m_UpdateTokenStack.Peek() != token)
|
||||||
@@ -94,65 +145,81 @@ namespace XericUI.XTable.Rendering.Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_UpdateContextStack.Push(context);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommitUpdateContext(context);
|
Dictionary<GameObject, Action<GameObject>> prefabCallbacks = CaptureActivePrefabReleaseCallbacks();
|
||||||
|
ExecuteUpdateCommands(context);
|
||||||
|
if (context.DirtyFlags == TableDirtyType.None) return;
|
||||||
|
|
||||||
|
m_UpdateContextStack.Pop();
|
||||||
|
ReleaseRenderedBindings(prefabCallbacks);
|
||||||
|
ApplyDirty(context.DirtyFlags);
|
||||||
|
RefreshView(false);
|
||||||
|
RefreshSelectionOverlays();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (m_UpdateContextStack.Count > 0 && ReferenceEquals(m_UpdateContextStack.Peek(), context))
|
||||||
|
m_UpdateContextStack.Pop();
|
||||||
ReturnUpdateContext(context);
|
ReturnUpdateContext(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExecuteUpdateCommands(UpdateContext context)
|
||||||
|
{
|
||||||
|
foreach (UpdateCommand command in context.Commands)
|
||||||
|
{
|
||||||
|
switch (command.Type)
|
||||||
|
{
|
||||||
|
case UpdateCommandType.SetFreezeCount: SetFreezeCountImmediate(command.Row, command.Col, false); break;
|
||||||
|
case UpdateCommandType.SetCellData: SetCellDataImmediate(command.Row, command.Col, command.CellData); break;
|
||||||
|
case UpdateCommandType.SetCellText: SetCellTextImmediate(command.Row, command.Col, command.StringValue1); break;
|
||||||
|
case UpdateCommandType.SetCellImage: SetCellImageImmediate(command.Row, command.Col, command.Texture); break;
|
||||||
|
case UpdateCommandType.SetCellPrefab: SetCellPrefabImmediate(command.Row, command.Col, command.Prefab); break;
|
||||||
|
case UpdateCommandType.ClearCellText: ClearCellTextImmediate(command.Row, command.Col); break;
|
||||||
|
case UpdateCommandType.ClearCellImage: ClearCellImageImmediate(command.Row, command.Col); break;
|
||||||
|
case UpdateCommandType.ClearCellPrefab: ClearCellPrefabImmediate(command.Row, command.Col); break;
|
||||||
|
case UpdateCommandType.SetCellStyle: SetCellStyleImmediate(command.Row, command.Col, command.StringValue1, command.StringValue2); break;
|
||||||
|
case UpdateCommandType.SetCellStyleNamespace: SetCellStyleNamespaceImmediate(command.Row, command.Col, command.StringValue1); break;
|
||||||
|
case UpdateCommandType.ClearCellStyle: ClearCellStyleImmediate(command.Row, command.Col); break;
|
||||||
|
case UpdateCommandType.MergeCells: MergeCellsImmediate(command.Row, command.Col, command.IntValue1, command.IntValue2); break;
|
||||||
|
case UpdateCommandType.UnmergeCells: UnmergeCellsImmediate(command.Row, command.Col); break;
|
||||||
|
case UpdateCommandType.AddRow: AddRowImmediate(command.FloatValue); break;
|
||||||
|
case UpdateCommandType.AddColumn: AddColumnImmediate(command.FloatValue); break;
|
||||||
|
case UpdateCommandType.RemoveRow: RemoveRowImmediate(command.Row); break;
|
||||||
|
case UpdateCommandType.RemoveColumn: RemoveColumnImmediate(command.Col); break;
|
||||||
|
case UpdateCommandType.SetRowHeight: SetRowHeightImmediate(command.Row, command.FloatValue); break;
|
||||||
|
case UpdateCommandType.SetColWidth: SetColWidthImmediate(command.Col, command.FloatValue); break;
|
||||||
|
case UpdateCommandType.ForceStyleRefresh: ForceStyleRefreshImmediate(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ReturnUpdateContext(UpdateContext context)
|
private void ReturnUpdateContext(UpdateContext context)
|
||||||
{
|
{
|
||||||
context.Reset();
|
context.Reset();
|
||||||
m_UpdateContextPool.Push(context);
|
m_UpdateContextPool.Push(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CommitUpdateContext(UpdateContext context)
|
private Dictionary<GameObject, Action<GameObject>> CaptureActivePrefabReleaseCallbacks()
|
||||||
{
|
{
|
||||||
if (context.DirtyFlags == TableDirtyType.None) return;
|
var callbacks = new Dictionary<GameObject, Action<GameObject>>();
|
||||||
|
if (m_ActivePrefabMap == null) return callbacks;
|
||||||
if (context.HasStructuralChange)
|
|
||||||
CommitStructuralRenderCleanup(context.InvalidatePrefabBindings, context.PrefabReleaseCallbacks);
|
|
||||||
|
|
||||||
ApplyDirty(context.DirtyFlags);
|
|
||||||
if (context.RequiresImmediateRefresh)
|
|
||||||
RefreshView();
|
|
||||||
RefreshSelectionOverlays();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RecordStructuralChange(bool invalidatePrefabBindings)
|
|
||||||
{
|
|
||||||
if (!IsCheckingUpdate) return;
|
|
||||||
UpdateContext context = m_UpdateContextStack.Peek();
|
|
||||||
context.HasStructuralChange = true;
|
|
||||||
context.InvalidatePrefabBindings |= invalidatePrefabBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CaptureActivePrefabReleaseCallbacks()
|
|
||||||
{
|
|
||||||
if (!IsCheckingUpdate || m_ActivePrefabMap == null) return;
|
|
||||||
var callbacks = m_UpdateContextStack.Peek().PrefabReleaseCallbacks;
|
|
||||||
foreach (var pair in m_ActivePrefabMap)
|
foreach (var pair in m_ActivePrefabMap)
|
||||||
{
|
{
|
||||||
var callback = TableData.GetCell(pair.Key.Item1, pair.Key.Item2)?.OnPrefabRelease;
|
Action<GameObject> callback = TableData.GetCell(pair.Key.Item1, pair.Key.Item2)?.OnPrefabRelease;
|
||||||
if (callback != null && !callbacks.ContainsKey(pair.Value))
|
if (callback != null && !callbacks.ContainsKey(pair.Value))
|
||||||
callbacks.Add(pair.Value, callback);
|
callbacks.Add(pair.Value, callback);
|
||||||
}
|
}
|
||||||
|
return callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RequestRefresh(TableDirtyType type, bool immediate)
|
private void RequestRefresh(TableDirtyType type, bool immediate)
|
||||||
{
|
{
|
||||||
MarkDirty(type);
|
MarkDirty(type);
|
||||||
if (IsCheckingUpdate)
|
if (IsCheckingUpdate) return;
|
||||||
{
|
if (immediate) RefreshView();
|
||||||
m_UpdateContextStack.Peek().RequiresImmediateRefresh |= immediate;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (immediate)
|
|
||||||
RefreshView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThrowIfCheckingUpdate(string operation)
|
private void ThrowIfCheckingUpdate(string operation)
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ namespace XericUI.XTable.Rendering.Elements
|
|||||||
if (parent != null && item.Root.transform.parent != parent)
|
if (parent != null && item.Root.transform.parent != parent)
|
||||||
item.Root.transform.SetParent(parent, false);
|
item.Root.transform.SetParent(parent, false);
|
||||||
item.CellId = cellId;
|
item.CellId = cellId;
|
||||||
|
item.Root.SetActive(true);
|
||||||
item.Root.name = name;
|
item.Root.name = name;
|
||||||
ResetImageDefaults(item);
|
ResetImageDefaults(item);
|
||||||
BindStyle(item.Style, styleNamespace);
|
BindStyle(item.Style, styleNamespace);
|
||||||
@@ -121,6 +122,7 @@ namespace XericUI.XTable.Rendering.Elements
|
|||||||
if (parent != null && item.Root.transform.parent != parent)
|
if (parent != null && item.Root.transform.parent != parent)
|
||||||
item.Root.transform.SetParent(parent, false);
|
item.Root.transform.SetParent(parent, false);
|
||||||
item.CellId = cellId;
|
item.CellId = cellId;
|
||||||
|
item.Root.SetActive(true);
|
||||||
item.Root.name = name;
|
item.Root.name = name;
|
||||||
ResetTextDefaults(item);
|
ResetTextDefaults(item);
|
||||||
BindStyle(item.Style, styleNamespace);
|
BindStyle(item.Style, styleNamespace);
|
||||||
@@ -148,30 +150,41 @@ namespace XericUI.XTable.Rendering.Elements
|
|||||||
OnStyleChangeDetected?.Invoke();
|
OnStyleChangeDetected?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>归还所有槽位至 FrozenSlots(全量刷新时调用)</summary>
|
/// <summary>归还所有槽位(全量刷新时调用)。被归还对象会隐藏,直到下次 Acquire。</summary>
|
||||||
public void ReturnAll()
|
public void ReturnAll()
|
||||||
{
|
{
|
||||||
// Unbind 所有活跃槽位的样式回调
|
m_ImagePool.ReturnAll(HideImage);
|
||||||
m_ImagePool.ReturnAll();
|
m_TextPool.ReturnAll(HideText);
|
||||||
m_TextPool.ReturnAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>归还单个 Image 池对象(通过 CellId 定位槽位并移入 FrozenSlots)</summary>
|
/// <summary>归还单个 Image 池对象并隐藏。</summary>
|
||||||
public void ReleaseImage(PooledImage item)
|
public void ReleaseImage(PooledImage item)
|
||||||
{
|
{
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
item.Style?.Unbind();
|
HideImage(item);
|
||||||
m_ImagePool.ReleaseByObject(item);
|
m_ImagePool.ReleaseByObject(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>归还单个 Text 池对象(通过 CellId 定位槽位并移入 FrozenSlots)</summary>
|
/// <summary>归还单个 Text 池对象并隐藏。</summary>
|
||||||
public void ReleaseText(PooledText item)
|
public void ReleaseText(PooledText item)
|
||||||
{
|
{
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
item.Style?.Unbind();
|
HideText(item);
|
||||||
m_TextPool.ReleaseByObject(item);
|
m_TextPool.ReleaseByObject(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void HideImage(PooledImage item)
|
||||||
|
{
|
||||||
|
item.Style?.Unbind();
|
||||||
|
item.Root.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HideText(PooledText item)
|
||||||
|
{
|
||||||
|
item.Style?.Unbind();
|
||||||
|
item.Root.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 默认值重置
|
#region 默认值重置
|
||||||
|
|||||||
@@ -1,195 +1,133 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace XericUI.XTable.Rendering.Elements
|
namespace XericUI.XTable.Rendering.Elements
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>按原型管理的预制体池。占用槽位绑定 cellId,归还后隐藏并留在同原型待命池中复用。</summary>
|
||||||
/// 预制体对象池——按原型分组管理,每个原型使用计数器槽位绑定 cellId 到预制体实例。
|
public class PrefabPrototypePool
|
||||||
/// 预制体不参与 FrozenSlots 流转:始终绑定在 cellId 槽位上,仅在表格销毁时释放。
|
{
|
||||||
/// </summary>
|
private class PrefabPool
|
||||||
public class PrefabPrototypePool
|
{
|
||||||
{
|
public readonly Dictionary<int, GameObject> ActiveSlots = new();
|
||||||
#region 内部类型
|
public readonly Stack<GameObject> Standby = new();
|
||||||
|
}
|
||||||
|
|
||||||
private class PrefabPool
|
private readonly Dictionary<GameObject, PrefabPool> m_Pools = new();
|
||||||
{
|
private readonly Transform m_Parent;
|
||||||
public GameObject Prototype;
|
private const int MAX_PREFAB_ITEMS = 256;
|
||||||
/// <summary>cellId → 预制体实例 的计数器槽位绑定</summary>
|
private int m_TotalCreated;
|
||||||
public Dictionary<int, GameObject> Slots = new Dictionary<int, GameObject>();
|
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
|
||||||
}
|
private static readonly Vector2 AnchorTopLeft = new(0, 1);
|
||||||
|
private static readonly Vector2 PivotTopLeft = new(0, 1);
|
||||||
|
|
||||||
#endregion
|
public PrefabPrototypePool(Transform parent) => m_Parent = parent;
|
||||||
|
|
||||||
#region 字段
|
public GameObject Acquire(GameObject prefab, int cellId, Transform parent = null)
|
||||||
|
{
|
||||||
|
if (prefab == null) return null;
|
||||||
|
PrefabPool pool = GetOrCreatePool(prefab);
|
||||||
|
Transform targetParent = parent ?? m_Parent;
|
||||||
|
if (pool.ActiveSlots.TryGetValue(cellId, out GameObject existing))
|
||||||
|
{
|
||||||
|
Prepare(existing, targetParent, cellId);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<GameObject, PrefabPool> m_Pools = new Dictionary<GameObject, PrefabPool>();
|
GameObject instance = pool.Standby.Count > 0 ? pool.Standby.Pop() : Create(prefab, targetParent);
|
||||||
private Transform m_Parent;
|
if (instance == null) return null;
|
||||||
|
pool.ActiveSlots[cellId] = instance;
|
||||||
|
Prepare(instance, targetParent, cellId);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>渲染项目总数上限(全局,跨所有原型)</summary>
|
public void ReturnAll(Action<GameObject> beforeRelease = null)
|
||||||
private const int MAX_PREFAB_ITEMS = 256;
|
{
|
||||||
private int m_TotalCreated;
|
foreach (PrefabPool pool in m_Pools.Values)
|
||||||
|
{
|
||||||
|
foreach (GameObject instance in pool.ActiveSlots.Values)
|
||||||
|
{
|
||||||
|
beforeRelease?.Invoke(instance);
|
||||||
|
instance.SetActive(false);
|
||||||
|
pool.Standby.Push(instance);
|
||||||
|
}
|
||||||
|
pool.ActiveSlots.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private const HideFlags POOL_FLAGS = HideFlags.DontSave | HideFlags.HideInHierarchy;
|
public bool Release(GameObject instance, Action<GameObject> beforeRelease = null)
|
||||||
|
{
|
||||||
|
if (instance == null) return false;
|
||||||
|
foreach (PrefabPool pool in m_Pools.Values)
|
||||||
|
{
|
||||||
|
int foundCellId = -1;
|
||||||
|
foreach (var pair in pool.ActiveSlots)
|
||||||
|
{
|
||||||
|
if (pair.Value != instance) continue;
|
||||||
|
foundCellId = pair.Key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (foundCellId < 0) continue;
|
||||||
|
beforeRelease?.Invoke(instance);
|
||||||
|
pool.ActiveSlots.Remove(foundCellId);
|
||||||
|
instance.SetActive(false);
|
||||||
|
pool.Standby.Push(instance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly Vector2 AnchorTopLeft = new Vector2(0, 1);
|
public void DestroyAll()
|
||||||
private static readonly Vector2 PivotTopLeft = new Vector2(0, 1);
|
{
|
||||||
|
foreach (PrefabPool pool in m_Pools.Values)
|
||||||
|
{
|
||||||
|
foreach (GameObject instance in pool.ActiveSlots.Values) KillObject(instance);
|
||||||
|
foreach (GameObject instance in pool.Standby) KillObject(instance);
|
||||||
|
}
|
||||||
|
m_Pools.Clear();
|
||||||
|
m_TotalCreated = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
private PrefabPool GetOrCreatePool(GameObject prefab)
|
||||||
|
{
|
||||||
|
if (!m_Pools.TryGetValue(prefab, out PrefabPool pool))
|
||||||
|
{
|
||||||
|
pool = new PrefabPool();
|
||||||
|
m_Pools.Add(prefab, pool);
|
||||||
|
}
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
#region 构造 / 销毁
|
private GameObject Create(GameObject prefab, Transform parent)
|
||||||
|
{
|
||||||
|
if (m_TotalCreated >= MAX_PREFAB_ITEMS)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[XericUIActionTable] Prefab 池已达上限 {MAX_PREFAB_ITEMS},无法分配新实例");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
GameObject instance = UnityEngine.Object.Instantiate(prefab, parent);
|
||||||
|
instance.hideFlags = POOL_FLAGS;
|
||||||
|
m_TotalCreated++;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
public PrefabPrototypePool(Transform parent)
|
private static void Prepare(GameObject instance, Transform parent, int cellId)
|
||||||
{
|
{
|
||||||
m_Parent = parent;
|
if (parent != null && instance.transform.parent != parent)
|
||||||
}
|
instance.transform.SetParent(parent, false);
|
||||||
|
instance.name = $"_prefab_{cellId}";
|
||||||
|
RectTransform rt = instance.GetComponent<RectTransform>() ?? instance.AddComponent<RectTransform>();
|
||||||
|
rt.anchorMin = AnchorTopLeft;
|
||||||
|
rt.anchorMax = AnchorTopLeft;
|
||||||
|
rt.pivot = PivotTopLeft;
|
||||||
|
instance.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>彻底销毁所有池对象(所有原型的活跃槽位)</summary>
|
private static void KillObject(GameObject go)
|
||||||
public void DestroyAll()
|
{
|
||||||
{
|
if (go == null) return;
|
||||||
foreach (var kv in m_Pools)
|
if (Application.isPlaying) UnityEngine.Object.Destroy(go);
|
||||||
{
|
else UnityEngine.Object.DestroyImmediate(go);
|
||||||
var pool = kv.Value;
|
}
|
||||||
foreach (var instance in pool.Slots.Values)
|
}
|
||||||
KillObject(instance);
|
|
||||||
pool.Slots.Clear();
|
|
||||||
}
|
|
||||||
m_Pools.Clear();
|
|
||||||
m_TotalCreated = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void KillObject(GameObject go)
|
|
||||||
{
|
|
||||||
if (go == null) return;
|
|
||||||
if (Application.isPlaying)
|
|
||||||
Object.Destroy(go);
|
|
||||||
else
|
|
||||||
Object.DestroyImmediate(go);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 池操作
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 为指定 cellId 获取预制体实例。
|
|
||||||
/// 若该 cellId 已有绑定的实例,直接返回;否则创建新实例(需未达上限)。
|
|
||||||
/// </summary>
|
|
||||||
public GameObject Acquire(GameObject prefab, int cellId, Transform parent = null)
|
|
||||||
{
|
|
||||||
if (prefab == null) return null;
|
|
||||||
|
|
||||||
var pool = GetOrCreatePool(prefab);
|
|
||||||
Transform targetParent = parent ?? m_Parent;
|
|
||||||
|
|
||||||
// 已绑定 → 直接复用并迁移到当前渲染区域。
|
|
||||||
if (pool.Slots.TryGetValue(cellId, out var existing))
|
|
||||||
{
|
|
||||||
if (targetParent != null && existing.transform.parent != targetParent)
|
|
||||||
existing.transform.SetParent(targetParent, false);
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建新实例
|
|
||||||
if (m_TotalCreated >= MAX_PREFAB_ITEMS)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[XericUIActionTable] Prefab 池已达上限 {MAX_PREFAB_ITEMS},无法分配新实例");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance = Object.Instantiate(prefab, targetParent);
|
|
||||||
instance.hideFlags = POOL_FLAGS;
|
|
||||||
instance.name = $"_prefab_{cellId}";
|
|
||||||
m_TotalCreated++;
|
|
||||||
|
|
||||||
// 强制 RectTransform 左上角坐标系
|
|
||||||
RectTransform rt = instance.GetComponent<RectTransform>();
|
|
||||||
if (rt == null) rt = instance.AddComponent<RectTransform>();
|
|
||||||
rt.anchorMin = AnchorTopLeft;
|
|
||||||
rt.anchorMax = AnchorTopLeft;
|
|
||||||
rt.pivot = PivotTopLeft;
|
|
||||||
|
|
||||||
pool.Slots[cellId] = instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 归还指定实例至槽位(仅从槽位移除 + 销毁对象,不在滚动时调用)。
|
|
||||||
/// 预制体的回收仅发生在表格销毁时。
|
|
||||||
/// </summary>
|
|
||||||
public void Release(GameObject instance)
|
|
||||||
{
|
|
||||||
if (instance == null) return;
|
|
||||||
|
|
||||||
foreach (var kv in m_Pools)
|
|
||||||
{
|
|
||||||
var pool = kv.Value;
|
|
||||||
// 查找并移除
|
|
||||||
int? foundKey = null;
|
|
||||||
foreach (var slot in pool.Slots)
|
|
||||||
{
|
|
||||||
if (slot.Value == instance)
|
|
||||||
{
|
|
||||||
foundKey = slot.Key;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundKey.HasValue)
|
|
||||||
{
|
|
||||||
pool.Slots.Remove(foundKey.Value);
|
|
||||||
m_TotalCreated--;
|
|
||||||
KillObject(instance);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 不属于任何池(异常情况),直接销毁
|
|
||||||
KillObject(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>销毁所有按 cellId 绑定的实例,并在销毁前通知调用方。</summary>
|
|
||||||
public void ReleaseAll(System.Action<GameObject> beforeRelease = null)
|
|
||||||
{
|
|
||||||
foreach (var pool in m_Pools.Values)
|
|
||||||
{
|
|
||||||
foreach (var instance in pool.Slots.Values)
|
|
||||||
{
|
|
||||||
beforeRelease?.Invoke(instance);
|
|
||||||
KillObject(instance);
|
|
||||||
}
|
|
||||||
pool.Slots.Clear();
|
|
||||||
}
|
|
||||||
m_Pools.Clear();
|
|
||||||
m_TotalCreated = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>结束当前渲染周期。实例保留在全局槽位中,以便跨区域复用。</summary>
|
|
||||||
public void ReturnAll()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>获取当前所有活跃实例(用于外部遍历回调)</summary>
|
|
||||||
public IReadOnlyCollection<GameObject> GetActive(GameObject prefab)
|
|
||||||
{
|
|
||||||
if (prefab == null || !m_Pools.TryGetValue(prefab, out var pool))
|
|
||||||
return System.Array.Empty<GameObject>();
|
|
||||||
return pool.Slots.Values;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 内部方法
|
|
||||||
|
|
||||||
private PrefabPool GetOrCreatePool(GameObject prefab)
|
|
||||||
{
|
|
||||||
if (!m_Pools.TryGetValue(prefab, out var pool))
|
|
||||||
{
|
|
||||||
pool = new PrefabPool { Prototype = prefab };
|
|
||||||
m_Pools[prefab] = pool;
|
|
||||||
}
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,15 +147,20 @@ namespace XericUI.XTable.Rendering.Elements
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>释放所有活跃槽位,但保留其所属计数器。</summary>
|
/// <summary>释放所有活跃槽位;回调在槽位变为空闲前执行。</summary>
|
||||||
public void ReturnAll()
|
public void ReturnAll(Action<T> beforeRelease = null)
|
||||||
{
|
{
|
||||||
for (int ci = 0; ci < POOL_SIZE; ci++)
|
for (int ci = 0; ci < POOL_SIZE; ci++)
|
||||||
{
|
{
|
||||||
var counter = m_Counters[ci];
|
var counter = m_Counters[ci];
|
||||||
if (counter == null) continue;
|
if (counter == null) continue;
|
||||||
for (int i = 0; i < counter.Slots.Count; i++)
|
for (int i = 0; i < counter.Slots.Count; i++)
|
||||||
counter.Slots[i].CellId = -1;
|
{
|
||||||
|
var slot = counter.Slots[i];
|
||||||
|
if (slot.CellId == -1) continue;
|
||||||
|
beforeRelease?.Invoke(slot.PooledObject);
|
||||||
|
slot.CellId = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user