Merge remote-tracking branch 'gitea/thr_dev/dev2'
# Conflicts: # Runtime/XTable/Rendering/Component/XericUIActionTable.cs
This commit is contained in:
@@ -2,6 +2,7 @@ using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
using XericLibrary.Runtime.SuperStyleSheet;
|
||||
using XericUI.XTable.Core;
|
||||
using XericUI.XTable.Rendering.Component;
|
||||
|
||||
@@ -177,9 +178,9 @@ namespace XericUIEditor.XTable
|
||||
if (GUILayout.Button("手动刷新视图", GUILayout.Height(22)))
|
||||
EditorApplication.delayCall += () => m_Table.RefreshView();
|
||||
|
||||
// ── 预制体配置 ──
|
||||
// ── 单元格配置 ──
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.LabelField("单元格预制体配置", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField("单元格配置", EditorStyles.boldLabel);
|
||||
int selRow = m_Table.SelectedRow;
|
||||
int selCol = m_Table.SelectedCol;
|
||||
if (selRow < 0 || selCol < 0)
|
||||
@@ -189,30 +190,79 @@ namespace XericUIEditor.XTable
|
||||
else
|
||||
{
|
||||
var data = m_Table.TableData?.GetCell(selRow, selCol);
|
||||
GameObject currentPrefab = data?.Prefab;
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.TextField("目标单元格", $"({selRow}, {selCol})");
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
GameObject newPrefab = (GameObject)EditorGUILayout.ObjectField(
|
||||
"预制体", currentPrefab, typeof(GameObject), false);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("应用预制体", GUILayout.Height(22)))
|
||||
if (data == null)
|
||||
{
|
||||
Undo.RecordObject(m_Table, "Set Cell Prefab");
|
||||
m_Table.SetCellPrefab(selRow, selCol, newPrefab);
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
Repaint();
|
||||
EditorGUILayout.HelpBox("单元格数据为空", MessageType.Warning);
|
||||
}
|
||||
if (GUILayout.Button("清除预制体", GUILayout.Height(22)))
|
||||
else
|
||||
{
|
||||
Undo.RecordObject(m_Table, "Clear Cell Prefab");
|
||||
m_Table.ClearCellPrefab(selRow, selCol);
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
Repaint();
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.TextField("目标单元格", $"({selRow}, {selCol})");
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
// 内容类型
|
||||
CellContentType ct = (CellContentType)EditorGUILayout.EnumPopup("内容类型", data.ContentType);
|
||||
|
||||
// 根据类型显示对应字段
|
||||
switch (ct)
|
||||
{
|
||||
case CellContentType.Text:
|
||||
EditorGUILayout.LabelField("文本内容", data.Text ?? "(空)");
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
EditorGUILayout.ObjectField("图片纹理", data.Image, typeof(Texture2D), false);
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
GameObject newPrefab = (GameObject)EditorGUILayout.ObjectField(
|
||||
"预制体", data.Prefab, typeof(GameObject), false);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(m_Table, "Set Cell Prefab");
|
||||
data.Prefab = newPrefab;
|
||||
data.ContentType = ct;
|
||||
m_Table.SetCellPrefab(selRow, selCol, newPrefab);
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
Repaint();
|
||||
}
|
||||
goto skipEndChangeCheck;
|
||||
}
|
||||
// 默认 Text 类型
|
||||
string newText = EditorGUILayout.TextField("文本内容", data.Text ?? "");
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(m_Table, "Edit Cell");
|
||||
data.Text = newText;
|
||||
data.ContentType = ct;
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
m_Table.MarkDirty(TableDirtyType.DataChanged);
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
Repaint();
|
||||
}
|
||||
skipEndChangeCheck:
|
||||
;
|
||||
|
||||
// 样式
|
||||
EditorGUILayout.Space(2);
|
||||
EditorGUILayout.LabelField("单元格样式 (StyleMember)", EditorStyles.miniBoldLabel);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
string ns = EditorGUILayout.TextField("命名空间",
|
||||
data.CellStyle?.CurrentNamespace ?? "");
|
||||
string path = EditorGUILayout.TextField("样式路径",
|
||||
data.CellStyle?.StylePath ?? "");
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(m_Table, "Edit Cell Style");
|
||||
if (data.CellStyle == null)
|
||||
data.CellStyle = new StyleMember();
|
||||
data.CellStyle.CurrentNamespace = ns;
|
||||
data.CellStyle.StylePath = path;
|
||||
EditorUtility.SetDirty(m_Table);
|
||||
m_Table.MarkDirty(TableDirtyType.DataChanged);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6a0a99a3d243d2429a9ca17b9b35149
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64d88208839a5034db31c330796453aa
|
||||
guid: 6da8337ecebc7074b9f37bc69feab5c1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -1,53 +1,84 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.SuperStyleSheet;
|
||||
|
||||
namespace XericUI.XTable.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 单元格数据模型——仅保存数据内容,不保存自身尺寸。
|
||||
/// 尺寸由渲染阶段的行列标题(行高/列宽)决定。
|
||||
/// 样式通过 StyleNamespace 字符串引用 StyleManager 中的命名空间。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class XTableCellData
|
||||
{
|
||||
/// <summary>文本内容</summary>
|
||||
public string Text;
|
||||
/// <summary>单元格内容类型——决定渲染时使用哪种内容</summary>
|
||||
public enum CellContentType
|
||||
{
|
||||
/// <summary>文本内容</summary>
|
||||
Text,
|
||||
/// <summary>图片纹理</summary>
|
||||
Image,
|
||||
/// <summary>预制体实例</summary>
|
||||
Prefab,
|
||||
}
|
||||
|
||||
/// <summary>图片纹理</summary>
|
||||
public Texture2D Image;
|
||||
/// <summary>
|
||||
/// 单元格数据模型——仅保存数据内容,不保存自身尺寸。
|
||||
/// 尺寸由渲染阶段的行列标题(行高/列宽)决定。
|
||||
/// 样式通过 StyleNamespace 字符串引用 StyleManager 中的命名空间。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class XTableCellData
|
||||
{
|
||||
/// <summary>内容类型——决定渲染时使用 Text / Image / Prefab 中的哪一个</summary>
|
||||
public CellContentType ContentType;
|
||||
|
||||
/// <summary>预制体对象(用于嵌入复杂 UI 元素)</summary>
|
||||
public GameObject Prefab;
|
||||
/// <summary>文本内容(ContentType == Text 时生效)</summary>
|
||||
public string Text;
|
||||
|
||||
/// <summary>
|
||||
/// 样式命名空间——对应 StyleManager 中的命名空间。
|
||||
/// 为空或 "xeric_table_default" 时使用默认表格样式。
|
||||
/// 渲染时从此命名空间读取 /cell/bg/color、/cell/font/size 等路径。
|
||||
/// </summary>
|
||||
public string StyleNamespace;
|
||||
/// <summary>图片纹理(ContentType == Image 时生效)</summary>
|
||||
public Texture2D Image;
|
||||
|
||||
/// <summary>预制体实例从对象池取出时回调——外部可在此初始化实例内容</summary>
|
||||
[NonSerialized] public System.Action<GameObject> OnPrefabAcquire;
|
||||
/// <summary>预制体对象(ContentType == Prefab 时生效,用于嵌入复杂 UI 元素)</summary>
|
||||
public GameObject Prefab;
|
||||
|
||||
/// <summary>预制体实例归还对象池前回调——外部可在此清理引用</summary>
|
||||
[NonSerialized] public System.Action<GameObject> OnPrefabRelease;
|
||||
/// <summary>
|
||||
/// 样式命名空间——对应 StyleManager 中的命名空间。
|
||||
/// 为空或 "xeric_table_default" 时使用默认表格样式。
|
||||
/// 渲染时从此命名空间读取 /cell/bg/color、/cell/font/size 等路径。
|
||||
/// </summary>
|
||||
public string StyleNamespace;
|
||||
|
||||
public XTableCellData()
|
||||
{ }
|
||||
/// <summary>
|
||||
/// 单元格级别样式覆盖——通过命名空间 + 路径引用样式表中的值。
|
||||
/// 渲染时优先使用此样式路径派生颜色/尺寸(如 {StylePath}/bg/color),未设置时回退到 StyleNamespace。
|
||||
/// </summary>
|
||||
public StyleMember CellStyle;
|
||||
|
||||
public XTableCellData(string text) => Text = text;
|
||||
/// <summary>预制体实例从对象池取出时回调——外部可在此初始化实例内容</summary>
|
||||
[NonSerialized] public System.Action<GameObject> OnPrefabAcquire;
|
||||
|
||||
public XTableCellData(string text, string styleNamespace = null) : this(text) =>
|
||||
StyleNamespace = styleNamespace;
|
||||
/// <summary>预制体实例归还对象池前回调——外部可在此清理引用</summary>
|
||||
[NonSerialized] public System.Action<GameObject> OnPrefabRelease;
|
||||
|
||||
public XTableCellData(string text, Texture2D image, string styleNamespace = null) :
|
||||
this(text, styleNamespace) => Image = image;
|
||||
public XTableCellData()
|
||||
{ }
|
||||
|
||||
public void ClearEvent()
|
||||
{
|
||||
OnPrefabAcquire = null;
|
||||
OnPrefabRelease = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public XTableCellData(string text) => Text = text;
|
||||
|
||||
public XTableCellData(string text, string styleNamespace = null) : this(text) =>
|
||||
StyleNamespace = styleNamespace;
|
||||
|
||||
public XTableCellData(string text, Texture2D image, string styleNamespace = null) :
|
||||
this(text, styleNamespace) => Image = image;
|
||||
|
||||
/// <summary>获取该单元格渲染时使用的样式命名空间(CellStyle 优先,否则 fallback 到 StyleNamespace)</summary>
|
||||
public string GetEffectiveStyleNamespace(string fallbackNamespace)
|
||||
{
|
||||
if (CellStyle != null && !string.IsNullOrEmpty(CellStyle.CurrentNamespace))
|
||||
return CellStyle.CurrentNamespace;
|
||||
if (!string.IsNullOrEmpty(StyleNamespace))
|
||||
return StyleNamespace;
|
||||
return fallbackNamespace;
|
||||
}
|
||||
|
||||
public void ClearEvent()
|
||||
{
|
||||
OnPrefabAcquire = null;
|
||||
OnPrefabRelease = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.XTable.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 表格系统全局配置资源——统一管理布局、样式回退值、运行时颜色等参数。
|
||||
/// 表格组件在 Awake 时自动加载 Resources 中的 "XTableDefaultConfig",
|
||||
/// 或由外部脚本在初始化阶段显式赋值。
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "XTableConfig", menuName = "Xeric Library/Table/Table Config", order = 100)]
|
||||
public class XTableConfig : ScriptableObject
|
||||
{
|
||||
#region 布局
|
||||
|
||||
[Header("布局")]
|
||||
[Tooltip("新增行时的默认行高(像素)")]
|
||||
public float DefaultRowHeight = 40f;
|
||||
|
||||
[Tooltip("新增列时的默认列宽(像素)")]
|
||||
public float DefaultColumnWidth = 100f;
|
||||
|
||||
[Tooltip("单元格内容水平内边距(左右各此值)")]
|
||||
public float CellHorizontalPadding = 4f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 四叉树
|
||||
|
||||
[Header("四叉树")]
|
||||
[Tooltip("单节点最大单元格数,超过此值强制空间分裂以提升可见性裁剪效率")]
|
||||
public int QuadtreeMaxCellsPerNode = 64;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 对象池
|
||||
|
||||
[Header("对象池")]
|
||||
[Tooltip("通用对象池初始容量")]
|
||||
public int PoolInitialCapacity = 64;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 样式回退值
|
||||
|
||||
[Header("样式回退值(当 StyleManager 中查不到时使用)")]
|
||||
[Tooltip("默认样式命名空间")]
|
||||
public string DefaultStyleNamespace = "xeric_table_default";
|
||||
|
||||
[Tooltip("单元格背景色")]
|
||||
public Color BgColor = new Color(0.18f, 0.18f, 0.18f, 1f);
|
||||
|
||||
[Tooltip("单元格前景色(文字颜色)")]
|
||||
public Color FgColor = new Color(0.05f, 0.05f, 0.05f);
|
||||
|
||||
[Tooltip("编辑器选中色")]
|
||||
public Color SelectionColor = new Color(0.2f, 0.5f, 1f, 0.3f);
|
||||
|
||||
[Tooltip("边框颜色")]
|
||||
public Color BorderColor = new Color(0.35f, 0.35f, 0.35f, 0.5f);
|
||||
|
||||
[Tooltip("边框宽度")]
|
||||
public float BorderWidth = 1f;
|
||||
|
||||
[Tooltip("字体大小")]
|
||||
public int FontSize = 14;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 运行时颜色
|
||||
|
||||
[Header("运行时选中/边框颜色")]
|
||||
[Tooltip("运行时单选高亮")]
|
||||
public Color RuntimeSelectionColor = new Color(0.18f, 0.42f, 0.82f, 0.25f);
|
||||
|
||||
[Tooltip("运行时多选高亮")]
|
||||
public Color RuntimeMultiSelectColor = new Color(0.18f, 0.42f, 0.82f, 0.12f);
|
||||
|
||||
[Tooltip("运行时焦点高亮(当前选中格)")]
|
||||
public Color RuntimeFocusColor = new Color(0.22f, 0.55f, 0.95f, 0.35f);
|
||||
|
||||
[Tooltip("运行时边框色")]
|
||||
public Color RuntimeBorderColor = new Color(0.35f, 0.35f, 0.35f, 0.5f);
|
||||
|
||||
[Tooltip("运行时边框宽度")]
|
||||
public float RuntimeBorderWidth = 1f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 编辑器多选颜色因子
|
||||
|
||||
[Header("编辑器多选/焦点颜色因子(基于 SelectionColor 计算)")]
|
||||
[Tooltip("多选透明因子")]
|
||||
[Range(0, 1)] public float MultiSelectAlphaFactor = 0.4f;
|
||||
|
||||
[Tooltip("焦点色 R 增量")]
|
||||
[Range(0, 0.5f)] public float FocusRAdd = 0.04f;
|
||||
|
||||
[Tooltip("焦点色 G 增量")]
|
||||
[Range(0, 0.5f)] public float FocusGAdd = 0.13f;
|
||||
|
||||
[Tooltip("焦点色 B 增量")]
|
||||
[Range(0, 0.5f)] public float FocusBAdd = 0.13f;
|
||||
|
||||
[Tooltip("焦点色 Alpha 倍数")]
|
||||
[Range(0.1f, 3f)] public float FocusAlphaMul = 1.4f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 资源路径
|
||||
|
||||
/// <summary>Resources 目录下的默认配置文件名(不含扩展名)</summary>
|
||||
public const string DEFAULT_RESOURCE_PATH = "XTableDefaultConfig";
|
||||
|
||||
/// <summary>从 Resources 加载默认配置,若失败则返回 ScriptableObject.CreateInstance 的默认值</summary>
|
||||
public static XTableConfig LoadOrDefault()
|
||||
{
|
||||
var cfg = Resources.Load<XTableConfig>(DEFAULT_RESOURCE_PATH);
|
||||
if (cfg != null) return cfg;
|
||||
return CreateInstance<XTableConfig>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94d64cced79c6ef41af29f7473ff5105
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -65,12 +65,17 @@ namespace XericUI.XTable.Core
|
||||
private Node m_Root;
|
||||
private float[] m_RowHeights;
|
||||
private float[] m_ColWidths;
|
||||
private int m_MaxCellsPerNode;
|
||||
|
||||
/// <summary>四叉树是否已构建完成</summary>
|
||||
public bool IsBuilt { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 常量
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构建
|
||||
|
||||
/// <summary>
|
||||
@@ -83,7 +88,7 @@ namespace XericUI.XTable.Core
|
||||
/// <param name="cells">所有有数据的单元格元数据列表</param>
|
||||
public void Build(float totalWidth, float totalHeight,
|
||||
float[] rowHeights, float[] colWidths,
|
||||
List<XTableQuadtreeCell> cells)
|
||||
List<XTableQuadtreeCell> cells, int maxCellsPerNode = 64)
|
||||
{
|
||||
if (rowHeights == null || colWidths == null || cells == null)
|
||||
{
|
||||
@@ -93,6 +98,7 @@ namespace XericUI.XTable.Core
|
||||
|
||||
m_RowHeights = rowHeights;
|
||||
m_ColWidths = colWidths;
|
||||
m_MaxCellsPerNode = maxCellsPerNode;
|
||||
m_Root = new Node { Region = new Rect(0, 0, totalWidth, totalHeight) };
|
||||
|
||||
int maxRow = rowHeights.Length - 1;
|
||||
@@ -109,10 +115,10 @@ namespace XericUI.XTable.Core
|
||||
/// <summary>强制重建(外部入口,通常由表格组件在数据/布局变更后调用)</summary>
|
||||
public void ForceRebuild(float totalWidth, float totalHeight,
|
||||
float[] rowHeights, float[] colWidths,
|
||||
List<XTableQuadtreeCell> cells)
|
||||
List<XTableQuadtreeCell> cells, int maxCellsPerNode = 64)
|
||||
{
|
||||
Clear();
|
||||
Build(totalWidth, totalHeight, rowHeights, colWidths, cells);
|
||||
Build(totalWidth, totalHeight, rowHeights, colWidths, cells, maxCellsPerNode);
|
||||
}
|
||||
|
||||
private void BuildNode(Node node, List<XTableQuadtreeCell> allCells,
|
||||
@@ -149,11 +155,20 @@ namespace XericUI.XTable.Core
|
||||
|
||||
if (allSame)
|
||||
{
|
||||
node.UniformNamespace = firstNs;
|
||||
node.Cells = new List<(int, int)>(cellsInRange.Count);
|
||||
for (int i = 0; i < cellsInRange.Count; i++)
|
||||
node.Cells.Add((cellsInRange[i].Row, cellsInRange[i].Col));
|
||||
return;
|
||||
// 命名空间统一,但若空间跨度过大则继续分裂以便按块跳过不可见区域
|
||||
if (cellsInRange.Count > m_MaxCellsPerNode &&
|
||||
(minRow < maxRow || minCol < maxCol))
|
||||
{
|
||||
// 继续执行分裂逻辑(不要 return)
|
||||
}
|
||||
else
|
||||
{
|
||||
node.UniformNamespace = firstNs;
|
||||
node.Cells = new List<(int, int)>(cellsInRange.Count);
|
||||
for (int i = 0; i < cellsInRange.Count; i++)
|
||||
node.Cells.Add((cellsInRange[i].Row, cellsInRange[i].Col));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 无法继续分裂(单行单列),强制标记为均匀
|
||||
|
||||
@@ -26,6 +26,16 @@ namespace XericUI.XTable.Rendering.Component
|
||||
/// <summary>共享活跃预制体映射表(由 XericUIActionTable 传入)</summary>
|
||||
public Dictionary<(int, int), GameObject> ActivePrefabMap;
|
||||
|
||||
/// <summary>Texture2D → Sprite 缓存,避免每帧分配</summary>
|
||||
private Dictionary<Texture2D, Sprite> m_SpriteCache;
|
||||
|
||||
/// <summary>单元格内容水平内边距(单侧),由 XericUIActionTable 设置</summary>
|
||||
public float CellPadding;
|
||||
private float CellPadding2 => CellPadding * 2f;
|
||||
|
||||
/// <summary>表格全局配置资源,由 XericUIActionTable 设置</summary>
|
||||
public XTableConfig Config;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公开属性
|
||||
@@ -191,8 +201,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
float borderWidth = GetStyleFloat(defaultNs, XericUIActionTable.STYLE_CELL_BORDER_BOTTOM_WIDTH,
|
||||
XericUIActionTable.BORDER_WIDTH_FALLBACK);
|
||||
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * 0.4f);
|
||||
Color focusColor = new Color(selectionColor.r + 0.04f, selectionColor.g + 0.13f, selectionColor.b + 0.13f, selectionColor.a * 1.4f);
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * Config.MultiSelectAlphaFactor);
|
||||
Color focusColor = new Color(selectionColor.r + Config.FocusRAdd, selectionColor.g + Config.FocusGAdd, selectionColor.b + Config.FocusBAdd, selectionColor.a * Config.FocusAlphaMul);
|
||||
|
||||
int selRow = GetSelectedRow();
|
||||
int selCol = GetSelectedCol();
|
||||
@@ -249,30 +259,46 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetStyleColor(cellNS, XericUIActionTable.STYLE_CELL_FG_COLOR,
|
||||
XericUIActionTable.FG_COLOR_FALLBACK);
|
||||
txt.Text.fontSize = GetStyleInt(cellNS, XericUIActionTable.STYLE_CELL_FONT_SIZE,
|
||||
XericUIActionTable.FONT_SIZE_FALLBACK);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetStyleFontAsset?.Invoke(cellNS,
|
||||
XericUIActionTable.STYLE_CELL_FONT_ASSET) ?? txt.Text.font;
|
||||
}
|
||||
|
||||
if (data.Prefab != null && PrefabPool != null)
|
||||
{
|
||||
GameObject instance = PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
if (ActivePrefabMap != null)
|
||||
ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetStyleColor(cellNS, XericUIActionTable.STYLE_CELL_FG_COLOR,
|
||||
XericUIActionTable.FG_COLOR_FALLBACK);
|
||||
txt.Text.fontSize = GetStyleInt(cellNS, XericUIActionTable.STYLE_CELL_FONT_SIZE,
|
||||
XericUIActionTable.FONT_SIZE_FALLBACK);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetStyleFontAsset?.Invoke(cellNS,
|
||||
XericUIActionTable.STYLE_CELL_FONT_ASSET) ?? txt.Text.font;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null && PrefabPool != null)
|
||||
{
|
||||
GameObject instance = PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
if (ActivePrefabMap != null)
|
||||
ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,9 +307,9 @@ namespace XericUI.XTable.Rendering.Component
|
||||
private void RenderCellsRuntime(int sr, int er, int sc, int ec,
|
||||
float[] rowHeights, float[] colWidths, XTableData tableData, string defaultNs)
|
||||
{
|
||||
Color rtSelectionColor = XericUIActionTable.RT_SELECTION_COLOR;
|
||||
Color rtMultiSelectColor = XericUIActionTable.RT_MULTI_SELECT_COLOR;
|
||||
Color rtFocusColor = XericUIActionTable.RT_FOCUS_COLOR;
|
||||
Color rtSelectionColor = Config.RuntimeSelectionColor;
|
||||
Color rtMultiSelectColor = Config.RuntimeMultiSelectColor;
|
||||
Color rtFocusColor = Config.RuntimeFocusColor;
|
||||
|
||||
int selRow = GetSelectedRow();
|
||||
int selCol = GetSelectedCol();
|
||||
@@ -328,33 +354,49 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
var hLine = m_Assembler.GetImage("cell_hline");
|
||||
hLine.Rect.anchoredPosition = new Vector2(cellX, cellY - cellH);
|
||||
hLine.Rect.sizeDelta = new Vector2(cellW, XericUIActionTable.RT_BORDER_WIDTH);
|
||||
hLine.Image.color = XericUIActionTable.RT_BORDER_COLOR;
|
||||
hLine.Rect.sizeDelta = new Vector2(cellW, Config.RuntimeBorderWidth);
|
||||
hLine.Image.color = Config.RuntimeBorderColor;
|
||||
|
||||
var vLine = m_Assembler.GetImage("cell_vline");
|
||||
vLine.Rect.anchoredPosition = new Vector2(cellX + cellW - XericUIActionTable.RT_BORDER_WIDTH, cellY);
|
||||
vLine.Rect.sizeDelta = new Vector2(XericUIActionTable.RT_BORDER_WIDTH, cellH);
|
||||
vLine.Image.color = XericUIActionTable.RT_BORDER_COLOR;
|
||||
vLine.Rect.anchoredPosition = new Vector2(cellX + cellW - Config.RuntimeBorderWidth, cellY);
|
||||
vLine.Rect.sizeDelta = new Vector2(Config.RuntimeBorderWidth, cellH);
|
||||
vLine.Image.color = Config.RuntimeBorderColor;
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
}
|
||||
|
||||
if (data.Prefab != null && PrefabPool != null)
|
||||
{
|
||||
GameObject instance = PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
if (ActivePrefabMap != null)
|
||||
ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null && PrefabPool != null)
|
||||
{
|
||||
GameObject instance = PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
if (ActivePrefabMap != null)
|
||||
ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,6 +404,26 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
/// <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 float CalcRowTopY(int row, float[] rowHeights)
|
||||
|
||||
@@ -10,7 +10,6 @@ using XericUI.XTable.Mapping;
|
||||
using XericUI.XTable.Rendering.Elements;
|
||||
using XericLibrary.Runtime.SuperStyleSheet;
|
||||
using XericLibrary.Runtime.UIGraph;
|
||||
using XericUI.XTable.Core;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
@@ -26,12 +25,6 @@ namespace XericUI.XTable.Rendering.Component
|
||||
[AddComponentMenu("Xeric UI Vessel/Table/Xeric UI Action Table", 60)]
|
||||
public partial class XericUIActionTable : XericUIBehaciour, IPointerClickHandler
|
||||
{
|
||||
#region 常量
|
||||
|
||||
private const string DEFAULT_STYLE_NS = "xeric_table_default";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 序列化字段
|
||||
|
||||
[SerializeField] private XTableData m_TableData;
|
||||
@@ -40,7 +33,11 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
[SerializeField] private float[] m_ColWidths = new float[] { 100f, 80f, 120f };
|
||||
|
||||
[SerializeField] private string m_DefaultStyleNamespace = DEFAULT_STYLE_NS;
|
||||
[SerializeField] private string m_DefaultStyleNamespace = "";
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("表格全局配置资源")]
|
||||
private XTableConfig m_Config;
|
||||
|
||||
[SerializeField] private ScrollRect m_ScrollRect;
|
||||
|
||||
@@ -76,6 +73,9 @@ namespace XericUI.XTable.Rendering.Component
|
||||
/// <summary>活跃预制体实例映射 (row, col) → instance,用于回调编排</summary>
|
||||
[System.NonSerialized] private Dictionary<(int, int), GameObject> m_ActivePrefabMap;
|
||||
|
||||
/// <summary>Texture2D → Sprite 缓存,避免每帧 Sprite.Create 分配</summary>
|
||||
[System.NonSerialized] private Dictionary<Texture2D, Sprite> m_SpriteCache;
|
||||
|
||||
/// <summary>表格四叉树——按 StyleNamespace 分区,渲染阶段查询可见象限</summary>
|
||||
[System.NonSerialized] private XTableQuadtree m_Quadtree;
|
||||
|
||||
@@ -186,6 +186,23 @@ namespace XericUI.XTable.Rendering.Component
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>表格全局配置资源</summary>
|
||||
public XTableConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Config == null)
|
||||
m_Config = XTableConfig.LoadOrDefault();
|
||||
return m_Config;
|
||||
}
|
||||
set => m_Config = value;
|
||||
}
|
||||
|
||||
/// <summary>单元格内容水平内边距(单侧)</summary>
|
||||
private float CellPadding => Config.CellHorizontalPadding;
|
||||
/// <summary>单元格内容宽度扣除量</summary>
|
||||
private float CellPadding2 => Config.CellHorizontalPadding * 2f;
|
||||
|
||||
/// <summary>
|
||||
/// 基于四叉树查询结果渲染可见象限内的单元格内容(文本/预制体/选择高亮)。
|
||||
/// 边框由 <see cref="RebuildBorders"/> 统一绘制。
|
||||
@@ -198,8 +215,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Color selectionColor = GetCellSelectionBgColor(ns);
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * 0.4f);
|
||||
Color focusColor = new Color(selectionColor.r + 0.04f, selectionColor.g + 0.13f, selectionColor.b + 0.13f, selectionColor.a * 1.4f);
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * Config.MultiSelectAlphaFactor);
|
||||
Color focusColor = new Color(selectionColor.r + Config.FocusRAdd, selectionColor.g + Config.FocusGAdd, selectionColor.b + Config.FocusBAdd, selectionColor.a * Config.FocusAlphaMul);
|
||||
|
||||
foreach (var q in quadrants)
|
||||
{
|
||||
@@ -215,6 +232,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
{
|
||||
// 跳过被合并覆盖的单元格
|
||||
if (TableData.IsCellMerged(row, col)) continue;
|
||||
// 跳过不可见的单元格
|
||||
if (!IsCellVisible(row, col)) continue;
|
||||
|
||||
float cellX = GetColLeftX(col) + m_ContentOffset;
|
||||
float cellY = -GetRowTopY(row) - m_ContentOffset;
|
||||
@@ -253,26 +272,42 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetCellFgColor(cellNS);
|
||||
txt.Text.fontSize = GetCellFontSize(cellNS);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetCellFontAsset(cellNS) ?? txt.Text.font;
|
||||
}
|
||||
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, row, col);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(row, col)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetCellFgColor(cellNS);
|
||||
txt.Text.fontSize = GetCellFontSize(cellNS);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetCellFontAsset(cellNS) ?? txt.Text.font;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, row, col);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(row, col)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,9 +317,9 @@ namespace XericUI.XTable.Rendering.Component
|
||||
#endif
|
||||
|
||||
// 运行时模式
|
||||
Color rtSelectionColor = RT_SELECTION_COLOR;
|
||||
Color rtMultiSelectColor = RT_MULTI_SELECT_COLOR;
|
||||
Color rtFocusColor = RT_FOCUS_COLOR;
|
||||
Color rtSelectionColor = Config.RuntimeSelectionColor;
|
||||
Color rtMultiSelectColor = Config.RuntimeMultiSelectColor;
|
||||
Color rtFocusColor = Config.RuntimeFocusColor;
|
||||
|
||||
foreach (var q in quadrants)
|
||||
{
|
||||
@@ -299,6 +334,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
{
|
||||
// 跳过被合并覆盖的单元格
|
||||
if (TableData.IsCellMerged(row, col)) continue;
|
||||
// 跳过不可见的单元格
|
||||
if (!IsCellVisible(row, col)) continue;
|
||||
|
||||
float cellX = GetColLeftX(col) + m_ContentOffset;
|
||||
float cellY = -GetRowTopY(row) - m_ContentOffset;
|
||||
@@ -336,22 +373,38 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
}
|
||||
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, row, col);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(row, col)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, row, col);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(row, col)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,13 +415,41 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
#region 生命周期
|
||||
|
||||
private static bool s_StyleSheetsPreloaded;
|
||||
|
||||
/// <summary>
|
||||
/// 预加载 Resources 目录下所有 XericStyleSheet 资产到 StyleManager。
|
||||
/// 仅首次调用生效,后续调用直接跳过。
|
||||
/// </summary>
|
||||
private static void PreloadAllStyleSheets()
|
||||
{
|
||||
if (s_StyleSheetsPreloaded) return;
|
||||
s_StyleSheetsPreloaded = true;
|
||||
|
||||
var sheets = Resources.LoadAll<XericStyleSheet>("");
|
||||
foreach (var sheet in sheets)
|
||||
{
|
||||
if (sheet != null)
|
||||
StyleManager.LoadStyleSheet(sheet);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
// 预加载 Resources 中所有 .xsss 样式表到 StyleManager(仅首次)
|
||||
PreloadAllStyleSheets();
|
||||
|
||||
// 域重载后旧子对象可能残留在场景中(DontSave 不自动销毁),先清空再重建
|
||||
ClearAllChildren();
|
||||
|
||||
if (m_Config == null)
|
||||
m_Config = XTableConfig.LoadOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(m_DefaultStyleNamespace))
|
||||
m_DefaultStyleNamespace = Config.DefaultStyleNamespace;
|
||||
|
||||
if (m_TableData == null)
|
||||
m_TableData = new XTableData();
|
||||
|
||||
@@ -520,38 +601,49 @@ namespace XericUI.XTable.Rendering.Component
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
}
|
||||
|
||||
/// <summary>设置文本内容(ContentType 自动设为 Text)</summary>
|
||||
public XTableCellData SetCellText(int row, int col, string text)
|
||||
{
|
||||
var data = TableData.GetOrCreateCell(row, col);
|
||||
data.Text = text;
|
||||
data.ContentType = CellContentType.Text;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
return data;
|
||||
}
|
||||
|
||||
public XTableCellData GetCellData(int row, int col)
|
||||
=> TableData.GetOrCreateCell(row, col);
|
||||
|
||||
/// <summary>为指定单元格设置预制体</summary>
|
||||
public XTableCellData SetCellPrefab(int row, int col, GameObject prefab)
|
||||
{
|
||||
var data = TableData.GetOrCreateCell(row, col);
|
||||
data.Prefab = prefab;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
/// <summary>设置图片纹理(ContentType 自动设为 Image)</summary>
|
||||
public XTableCellData SetCellImage(int row, int col, Texture2D image)
|
||||
{
|
||||
var data = TableData.GetOrCreateCell(row, col);
|
||||
data.Image = image;
|
||||
data.ContentType = CellContentType.Image;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>清除指定单元格的预制体</summary>
|
||||
public void ClearCellPrefab(int row, int col)
|
||||
{
|
||||
var data = TableData.GetCell(row, col);
|
||||
if (data == null) return;
|
||||
data.Prefab = null;
|
||||
data.OnPrefabAcquire = null;
|
||||
data.OnPrefabRelease = null;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
}
|
||||
/// <summary>设置预制体(ContentType 自动设为 Prefab)</summary>
|
||||
public XTableCellData SetCellPrefab(int row, int col, GameObject prefab)
|
||||
{
|
||||
var data = TableData.GetOrCreateCell(row, col);
|
||||
data.Prefab = prefab;
|
||||
data.ContentType = CellContentType.Prefab;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>设置单元格的样式命名空间,同时写入边框缓存(谁最后设置归谁)</summary>
|
||||
public XTableCellData SetCellStyle(int row, int col, string styleNamespace, string stylePath)
|
||||
{
|
||||
var data = TableData.GetOrCreateCell(row, col);
|
||||
if (data.CellStyle == null)
|
||||
data.CellStyle = new XericLibrary.Runtime.SuperStyleSheet.StyleMember();
|
||||
data.CellStyle.CurrentNamespace = styleNamespace ?? "";
|
||||
data.CellStyle.StylePath = stylePath ?? "";
|
||||
MarkDirty(TableDirtyType.StyleChanged);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>设置单元格的样式命名空间(兼容旧版,等同于 StyleNamespace 字段)</summary>
|
||||
public void SetCellStyleNamespace(int row, int col, string styleNamespace)
|
||||
{
|
||||
// 合并源:遍历所有被合并单元格,全部写入该样式
|
||||
@@ -583,10 +675,54 @@ namespace XericUI.XTable.Rendering.Component
|
||||
TableData.SetBorderBottomNs(row, col, ns);
|
||||
}
|
||||
|
||||
/// <summary>获取单元格的样式命名空间</summary>
|
||||
/// <summary>清除文本内容</summary>
|
||||
public void ClearCellText(int row, int col)
|
||||
{
|
||||
var data = TableData.GetCell(row, col);
|
||||
if (data == null) return;
|
||||
data.Text = null;
|
||||
if (data.ContentType == CellContentType.Text)
|
||||
data.ContentType = CellContentType.Text; // 保持类型不变,仅清空内容
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
}
|
||||
|
||||
/// <summary>清除图片纹理</summary>
|
||||
public void ClearCellImage(int row, int col)
|
||||
{
|
||||
var data = TableData.GetCell(row, col);
|
||||
if (data == null) return;
|
||||
data.Image = null;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
}
|
||||
|
||||
/// <summary>清除预制体及委托</summary>
|
||||
public void ClearCellPrefab(int row, int col)
|
||||
{
|
||||
var data = TableData.GetCell(row, col);
|
||||
if (data == null) return;
|
||||
data.Prefab = null;
|
||||
data.OnPrefabAcquire = null;
|
||||
data.OnPrefabRelease = null;
|
||||
MarkDirty(TableDirtyType.DataChanged);
|
||||
}
|
||||
|
||||
/// <summary>清除单元格样式覆盖</summary>
|
||||
public void ClearCellStyle(int row, int col)
|
||||
{
|
||||
var data = TableData.GetCell(row, col);
|
||||
if (data == null) return;
|
||||
data.CellStyle = null;
|
||||
MarkDirty(TableDirtyType.StyleChanged);
|
||||
}
|
||||
|
||||
public XTableCellData GetCellData(int row, int col)
|
||||
=> TableData.GetOrCreateCell(row, col);
|
||||
|
||||
/// <summary>获取单元格的样式命名空间(含回退逻辑)</summary>
|
||||
public string GetCellStyleNamespace(int row, int col)
|
||||
{
|
||||
return TableData.GetCell(row, col)?.StyleNamespace ?? m_DefaultStyleNamespace;
|
||||
var data = TableData.GetCell(row, col);
|
||||
return data != null ? data.GetEffectiveStyleNamespace(m_DefaultStyleNamespace) : m_DefaultStyleNamespace;
|
||||
}
|
||||
|
||||
public void MergeCells(int startRow, int startCol, int rowSpan, int colSpan)
|
||||
@@ -608,9 +744,10 @@ namespace XericUI.XTable.Rendering.Component
|
||||
public bool IsMergeSource(int row, int col, out int rowSpan, out int colSpan)
|
||||
=> TableData.IsMergeSource(row, col, out rowSpan, out colSpan);
|
||||
|
||||
/// <summary>在末尾追加一行,默认行高 40</summary>
|
||||
public void AddRow(float height = 40f)
|
||||
/// <summary>在末尾追加一行,默认行高来自 Config</summary>
|
||||
public void AddRow(float height = -1f)
|
||||
{
|
||||
if (height <= 0) height = Config.DefaultRowHeight;
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Undo.RecordObject(this, "Add Table Row");
|
||||
#endif
|
||||
@@ -625,9 +762,10 @@ namespace XericUI.XTable.Rendering.Component
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>在末尾追加一列,默认列宽 100</summary>
|
||||
public void AddColumn(float width = 100f)
|
||||
/// <summary>在末尾追加一列,默认列宽来自 Config</summary>
|
||||
public void AddColumn(float width = -1f)
|
||||
{
|
||||
if (width <= 0) width = Config.DefaultColumnWidth;
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Undo.RecordObject(this, "Add Table Column");
|
||||
#endif
|
||||
@@ -869,6 +1007,11 @@ namespace XericUI.XTable.Rendering.Component
|
||||
RecalculateTotalSize();
|
||||
UpdateVisibleRect();
|
||||
|
||||
// 确保 ScrollRect Content 尺寸正确(否则滚动时可能裁剪掉全部内容)
|
||||
RectTransform ctRt = ContentTransform as RectTransform;
|
||||
if (ctRt != null)
|
||||
ctRt.sizeDelta = new Vector2(m_TotalWidth, m_TotalHeight);
|
||||
|
||||
// 更新背景尺寸
|
||||
if (m_BackgroundGo != null)
|
||||
{
|
||||
@@ -898,12 +1041,14 @@ namespace XericUI.XTable.Rendering.Component
|
||||
Transform poolRoot = m_Assembler.PoolRoot;
|
||||
Transform prefabPoolRoot = m_PrefabPool?.PoolRoot;
|
||||
Transform contentT = ContentTransform;
|
||||
Transform borderT = contentT.Find("_TableBorders");
|
||||
for (int i = contentT.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
var child = contentT.GetChild(i);
|
||||
if (poolRoot != null && child == poolRoot) continue;
|
||||
if (prefabPoolRoot != null && child == prefabPoolRoot) continue;
|
||||
if (m_BackgroundGo != null && child.gameObject == m_BackgroundGo) continue;
|
||||
if (borderT != null && child == borderT) continue;
|
||||
|
||||
child.gameObject.hideFlags = HideFlags.None;
|
||||
if (Application.isPlaying)
|
||||
@@ -1171,8 +1316,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
Color borderColor = GetCellBorderColor(ns);
|
||||
float borderWidth = GetCellBorderWidth(ns);
|
||||
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * 0.4f);
|
||||
Color focusColor = new Color(selectionColor.r + 0.04f, selectionColor.g + 0.13f, selectionColor.b + 0.13f, selectionColor.a * 1.4f);
|
||||
Color multiSelectColor = new Color(selectionColor.r, selectionColor.g, selectionColor.b, selectionColor.a * Config.MultiSelectAlphaFactor);
|
||||
Color focusColor = new Color(selectionColor.r + Config.FocusRAdd, selectionColor.g + Config.FocusGAdd, selectionColor.b + Config.FocusBAdd, selectionColor.a * Config.FocusAlphaMul);
|
||||
|
||||
for (int r = sr; r < er && r < m_RowHeights.Length; r++)
|
||||
{
|
||||
@@ -1230,27 +1375,42 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetCellFgColor(cellNS);
|
||||
txt.Text.fontSize = GetCellFontSize(cellNS);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetCellFontAsset(cellNS) ?? txt.Text.font;
|
||||
}
|
||||
|
||||
// 预制体渲染
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
txt.Text.color = GetCellFgColor(cellNS);
|
||||
txt.Text.fontSize = GetCellFontSize(cellNS);
|
||||
txt.Text.alignment = TextAlignmentOptions.Midline;
|
||||
txt.Text.font = GetCellFontAsset(cellNS) ?? txt.Text.font;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1259,9 +1419,9 @@ namespace XericUI.XTable.Rendering.Component
|
||||
#endif
|
||||
|
||||
// 运行时模式
|
||||
Color rtSelectionColor = RT_SELECTION_COLOR;
|
||||
Color rtMultiSelectColor = RT_MULTI_SELECT_COLOR;
|
||||
Color rtFocusColor = RT_FOCUS_COLOR;
|
||||
Color rtSelectionColor = Config.RuntimeSelectionColor;
|
||||
Color rtMultiSelectColor = Config.RuntimeMultiSelectColor;
|
||||
Color rtFocusColor = Config.RuntimeFocusColor;
|
||||
|
||||
for (int r = sr; r < er && r < m_RowHeights.Length; r++)
|
||||
{
|
||||
@@ -1306,34 +1466,49 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
var hLine = m_Assembler.GetImage("cell_hline");
|
||||
hLine.Rect.anchoredPosition = new Vector2(cellX, cellY - cellH);
|
||||
hLine.Rect.sizeDelta = new Vector2(cellW, RT_BORDER_WIDTH);
|
||||
hLine.Image.color = RT_BORDER_COLOR;
|
||||
hLine.Rect.sizeDelta = new Vector2(cellW, Config.RuntimeBorderWidth);
|
||||
hLine.Image.color = Config.RuntimeBorderColor;
|
||||
|
||||
var vLine = m_Assembler.GetImage("cell_vline");
|
||||
vLine.Rect.anchoredPosition = new Vector2(cellX + cellW - RT_BORDER_WIDTH, cellY);
|
||||
vLine.Rect.sizeDelta = new Vector2(RT_BORDER_WIDTH, cellH);
|
||||
vLine.Image.color = RT_BORDER_COLOR;
|
||||
vLine.Rect.anchoredPosition = new Vector2(cellX + cellW - Config.RuntimeBorderWidth, cellY);
|
||||
vLine.Rect.sizeDelta = new Vector2(Config.RuntimeBorderWidth, cellH);
|
||||
vLine.Image.color = Config.RuntimeBorderColor;
|
||||
|
||||
if (data == null) continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
switch (data.ContentType)
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + 4, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - 8, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
// 运行时样式由 StyleBoundElement 被动更新,此处仅设置文本内容
|
||||
}
|
||||
|
||||
// 预制体渲染
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
case CellContentType.Text:
|
||||
if (!string.IsNullOrEmpty(data.Text))
|
||||
{
|
||||
var txt = m_Assembler.GetText("cell_text", cellNS);
|
||||
txt.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
txt.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
txt.Text.text = data.Text;
|
||||
// 运行时样式由 StyleBoundElement 被动更新,此处仅设置文本内容
|
||||
}
|
||||
break;
|
||||
case CellContentType.Image:
|
||||
if (data.Image != null)
|
||||
{
|
||||
Sprite sprite = GetOrCreateSprite(data.Image);
|
||||
var img = m_Assembler.GetImage("cell_image", cellNS);
|
||||
img.Rect.anchoredPosition = new Vector2(cellX + CellPadding, cellY);
|
||||
img.Rect.sizeDelta = new Vector2(cellW - CellPadding2, cellH);
|
||||
img.Image.sprite = sprite;
|
||||
}
|
||||
break;
|
||||
case CellContentType.Prefab:
|
||||
if (data.Prefab != null)
|
||||
{
|
||||
GameObject instance = m_PrefabPool.Acquire(data.Prefab, r, c);
|
||||
RectTransform prt = instance.GetComponent<RectTransform>();
|
||||
prt.anchoredPosition = new Vector2(cellX, cellY);
|
||||
prt.sizeDelta = new Vector2(cellW, cellH);
|
||||
m_ActivePrefabMap[(r, c)] = instance;
|
||||
data.OnPrefabAcquire?.Invoke(instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1343,6 +1518,18 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
#region 坐标计算
|
||||
|
||||
/// <summary>判断指定单元格是否与当前可见矩形有重叠(用于 RenderQuadrants 裁剪)</summary>
|
||||
private bool IsCellVisible(int row, int col)
|
||||
{
|
||||
float cellTop = GetRowTopY(row);
|
||||
float cellBottom = cellTop + m_RowHeights[row];
|
||||
float cellLeft = GetColLeftX(col);
|
||||
float cellRight = cellLeft + m_ColWidths[col];
|
||||
|
||||
return cellBottom > m_VisibleRect.y && cellTop < m_VisibleRect.yMax &&
|
||||
cellRight > m_VisibleRect.x && cellLeft < m_VisibleRect.xMax;
|
||||
}
|
||||
|
||||
private float GetRowTopY(int row)
|
||||
{
|
||||
float y = 0;
|
||||
@@ -1359,6 +1546,22 @@ namespace XericUI.XTable.Rendering.Component
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
private void RecalculateTotalSize()
|
||||
{
|
||||
m_TotalWidth = 0;
|
||||
@@ -1499,7 +1702,7 @@ namespace XericUI.XTable.Rendering.Component
|
||||
|
||||
#region 其他
|
||||
|
||||
private void MarkDirty(TableDirtyType type)
|
||||
public void MarkDirty(TableDirtyType type)
|
||||
{
|
||||
m_DirtyFlag.Mark(type);
|
||||
|
||||
@@ -1550,7 +1753,7 @@ namespace XericUI.XTable.Rendering.Component
|
||||
}
|
||||
}
|
||||
|
||||
m_Quadtree.Build(m_TotalWidth, m_TotalHeight, m_RowHeights, m_ColWidths, cells);
|
||||
m_Quadtree.Build(m_TotalWidth, m_TotalHeight, m_RowHeights, m_ColWidths, cells, Config.QuadtreeMaxCellsPerNode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1621,6 +1824,24 @@ namespace XericUI.XTable.Rendering.Component
|
||||
var borderRt = renderer.GetComponent<RectTransform>();
|
||||
borderRt.sizeDelta = new Vector2(m_TotalWidth, m_TotalHeight);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Color borderColor;
|
||||
float borderWidth;
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
borderColor = GetCellBorderColor(m_DefaultStyleNamespace);
|
||||
borderWidth = GetCellBorderWidth(m_DefaultStyleNamespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
borderColor = Config.RuntimeBorderColor;
|
||||
borderWidth = Config.RuntimeBorderWidth;
|
||||
}
|
||||
#else
|
||||
Color borderColor = Config.RuntimeBorderColor;
|
||||
float borderWidth = Config.RuntimeBorderWidth;
|
||||
#endif
|
||||
|
||||
int rows = m_RowHeights.Length;
|
||||
int cols = m_ColWidths.Length;
|
||||
|
||||
@@ -1759,6 +1980,8 @@ namespace XericUI.XTable.Rendering.Component
|
||||
// 共享预制体对象池
|
||||
m_ViewportRenderer.PrefabPool = m_PrefabPool;
|
||||
m_ViewportRenderer.ActivePrefabMap = m_ActivePrefabMap;
|
||||
m_ViewportRenderer.CellPadding = Config.CellHorizontalPadding;
|
||||
m_ViewportRenderer.Config = Config;
|
||||
}
|
||||
|
||||
/// <summary>渲染 Viewport 层冻结单元格,随滚动偏移调整位置</summary>
|
||||
|
||||
Reference in New Issue
Block a user