单元格内添加样式属性字段,以及内容类型切换的功能。

This commit is contained in:
2026-07-09 19:22:09 +08:00
parent cbae052e0f
commit 0c89d93ee2
4 changed files with 466 additions and 194 deletions
+72 -22
View File
@@ -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--;
+69 -38
View File
@@ -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;
}
}
}
@@ -26,6 +26,9 @@ namespace XericUI.XTable.Rendering.Component
/// <summary>共享活跃预制体映射表(由 XericUIActionTable 传入)</summary>
public Dictionary<(int, int), GameObject> ActivePrefabMap;
/// <summary>Texture2D → Sprite 缓存,避免每帧分配</summary>
private Dictionary<Texture2D, Sprite> m_SpriteCache;
#endregion
#region 公开属性
@@ -249,30 +252,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 + 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;
}
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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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;
}
}
}
@@ -338,23 +357,39 @@ 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 && 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 + 4, cellY);
txt.Rect.sizeDelta = new Vector2(cellW - 8, 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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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 +397,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)
@@ -76,6 +76,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;
@@ -250,26 +253,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 + 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;
}
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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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;
}
}
}
@@ -333,22 +352,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 + 4, cellY);
txt.Rect.sizeDelta = new Vector2(cellW - 8, 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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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;
}
}
}
@@ -517,38 +552,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>
/// <summary>设置单元格样式覆盖(StyleMember:命名空间 + 路径)</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)
{
var data = TableData.GetOrCreateCell(row, col);
@@ -556,10 +602,54 @@ namespace XericUI.XTable.Rendering.Component
MarkDirty(TableDirtyType.StyleChanged);
}
/// <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)
@@ -1201,27 +1291,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 + 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;
}
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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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;
}
}
}
@@ -1287,24 +1392,39 @@ 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;
// 运行时样式由 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 + 4, cellY);
txt.Rect.sizeDelta = new Vector2(cellW - 8, 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 + 4, cellY);
img.Rect.sizeDelta = new Vector2(cellW - 8, 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;
}
}
}
@@ -1330,6 +1450,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;
@@ -1466,7 +1602,7 @@ namespace XericUI.XTable.Rendering.Component
#region 其他
private void MarkDirty(TableDirtyType type)
public void MarkDirty(TableDirtyType type)
{
m_DirtyFlag.Mark(type);