Files

243 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using UnityEngine;
using XericUI.Core.Base;
using XericUI.XTable.Core;
namespace XericUI.XTable.Rendering.Component
{
/// <summary>
/// 表格数据与坐标计算——可见性判定、坐标公式、尺寸重算、四叉树维护。
/// </summary>
public partial class XericUIActionTable
{
#region 可见性判定
/// <summary>判断指定单元格是否与当前可见矩形有重叠(用于裁剪剔除)</summary>
private bool IsCellVisible(int row, int col)
{
// 计入 m_ContentOffset 以匹配单元格在 Content 中的实际渲染位置
float cellLeft = GetColLeftX(col) + m_ContentOffset;
float cellRight = cellLeft + m_ResolvedColWidths[col];
float cellTop = GetRowTopY(row) + m_ContentOffset;
float cellBottom = cellTop + m_ResolvedRowHeights[row];
return cellRight > m_VisibleRect.x && cellLeft < m_VisibleRect.xMax &&
cellBottom > m_VisibleRect.y && cellTop < m_VisibleRect.yMax;
}
#endregion
#region 渲染分区
private bool IsNormalRegionCell(int row, int col)
{
return row >= Mathf.Clamp(m_FreezeRowCount, 0, m_RowHeights?.Length ?? 0)
&& col >= Mathf.Clamp(m_FreezeColCount, 0, m_ColWidths?.Length ?? 0);
}
#endregion
#region 坐标公式
private float GetRowTopY(int row)
{
float y = 0;
for (int i = 0; i < row && i < m_ResolvedRowHeights.Length; i++)
y += m_ResolvedRowHeights[i];
return y;
}
private float GetColLeftX(int col)
{
float x = 0;
for (int i = 0; i < col && i < m_ResolvedColWidths.Length; i++)
x += m_ResolvedColWidths[i];
return x;
}
#endregion
#region Sprite 缓存
/// <summary>将 Texture2D 转为 Sprite(带缓存),用于 Image 类型单元格渲染</summary>
private Sprite GetOrCreateSprite(Texture2D texture)
{
if (texture == null) return null;
if (m_SpriteCache == null)
m_SpriteCache = new Dictionary<Texture2D, Sprite>();
if (!m_SpriteCache.TryGetValue(texture, out Sprite sprite))
{
sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
m_SpriteCache[texture] = sprite;
}
return sprite;
}
#endregion
#region 尺寸计算与可见矩形
private void RecalculateTotalSize()
{
m_TotalWidth = 0;
if (m_ResolvedColWidths != null)
foreach (float w in m_ResolvedColWidths)
m_TotalWidth += w;
m_TotalHeight = 0;
if (m_ResolvedRowHeights != null)
foreach (float h in m_ResolvedRowHeights)
m_TotalHeight += h;
m_ContentOffset = GetCellBorderWidth(m_DefaultStyleNamespace) * 0.5f;
float pad = m_ContentOffset * 2f;
if (m_ScrollRect != null && m_ScrollRect.content != null)
m_ScrollRect.content.sizeDelta = new Vector2(
m_TotalWidth + pad, m_TotalHeight + pad);
}
private void UpdateVisibleRect()
{
m_VisibleRect = new Rect(0, 0, m_TotalWidth, m_TotalHeight);
if (m_ScrollRect == null || m_ScrollRect.viewport == null || m_ScrollRect.content == null)
return;
RectTransform vt = m_ScrollRect.viewport;
RectTransform ct = m_ScrollRect.content as RectTransform;
if (vt == null || ct == null) return;
// Content 的 anchor/pivot 在 Awake 中强制设为 (0,1)anchoredPosition
// 直接表示 Content 左上角相对于 Viewport 左上角的偏移。
Vector2 ap = ct.anchoredPosition;
m_VisibleRect = new Rect(ap.x, ap.y, vt.rect.width, vt.rect.height);
}
private void CalcVisibleRange(out int sr, out int er, out int sc, out int ec)
{
sr = 0;
er = 0;
sc = 0;
ec = 0;
float accY = 0;
for (int r = 0; r < m_ResolvedRowHeights.Length; r++)
{
float rowBottom = accY + m_ResolvedRowHeights[r];
if (rowBottom > m_VisibleRect.y && accY < m_VisibleRect.yMax)
{
if (er == 0 && sr == 0) sr = r;
er = r + 1;
}
accY = rowBottom;
if (accY > m_VisibleRect.yMax) break;
}
float accX = 0;
for (int c = 0; c < m_ResolvedColWidths.Length; c++)
{
float colRight = accX + m_ResolvedColWidths[c];
if (colRight > m_VisibleRect.x && accX < m_VisibleRect.xMax)
{
if (ec == 0 && sc == 0) sc = c;
ec = c + 1;
}
accX = colRight;
if (accX > m_VisibleRect.xMax) break;
}
if (sr < 0) sr = 0;
if (sc < 0) sc = 0;
}
#endregion
#region 四叉树
/// <summary>强制重建四叉树(仅多样式表会实际构建)。</summary>
public void ForceRebuildQuadtree()
{
m_QuadtreeDirty = true;
EnsureQuadtreeBuilt();
}
private string ResolveCellNamespace(XTableCellData data)
{
return data != null ? data.GetEffectiveStyleNamespace(m_DefaultStyleNamespace) : m_DefaultStyleNamespace;
}
private bool HasMultipleStyleNamespaces()
{
RefreshActiveStyleNamespaces();
return m_ActiveStyleNamespaces.Count > 1;
}
private string GetSingleStyleNamespace()
{
RefreshActiveStyleNamespaces();
foreach (string ns in m_ActiveStyleNamespaces) return ns;
return m_DefaultStyleNamespace;
}
private void RefreshActiveStyleNamespaces()
{
if (!m_StyleNamespaceDirty) return;
m_ActiveStyleNamespaces.Clear();
if (m_TableData == null || m_RowHeights == null || m_ColWidths == null)
{
m_ActiveStyleNamespaces.Add(m_DefaultStyleNamespace);
m_StyleNamespaceDirty = false;
return;
}
for (int r = 0; r < m_RowHeights.Length; r++)
for (int c = 0; c < m_ColWidths.Length; c++)
{
if (m_TableData.IsCellMerged(r, c)) continue;
m_ActiveStyleNamespaces.Add(ResolveCellNamespace(m_TableData.GetCell(r, c)));
}
if (m_ActiveStyleNamespaces.Count == 0)
m_ActiveStyleNamespaces.Add(m_DefaultStyleNamespace);
m_StyleNamespaceDirty = false;
}
/// <summary>只在多样式背景分区需要时,从当前数据构建四叉树。</summary>
private void EnsureQuadtreeBuilt()
{
if (!HasMultipleStyleNamespaces())
{
m_Quadtree?.Clear();
m_QuadtreeDirty = false;
return;
}
if (!m_QuadtreeDirty && m_Quadtree != null && m_Quadtree.IsBuilt) return;
MaintainQuadtree();
}
private void MaintainQuadtree()
{
if (m_TableData == null || m_RowHeights == null || m_ColWidths == null) return;
if (m_Quadtree == null) m_Quadtree = new XTableQuadtree();
var cells = new List<XTableQuadtreeCell>();
for (int r = 0; r < m_RowHeights.Length; r++)
for (int c = 0; c < m_ColWidths.Length; c++)
{
if (m_TableData.IsCellMerged(r, c)) continue;
var data = m_TableData.GetCell(r, c);
cells.Add(new XTableQuadtreeCell { Row = r, Col = c, Namespace = ResolveCellNamespace(data) });
}
m_Quadtree.Build(m_TotalWidth, m_TotalHeight, m_ResolvedRowHeights, m_ResolvedColWidths, cells, Config.QuadtreeMaxCellsPerNode);
m_QuadtreeDirty = false;
}
#endregion
}
}