2 Commits
Author SHA1 Message Date
lrss3 b534795dcf 冻结行列添加偏移量与表格对齐 2026-08-18 15:17:21 +08:00
lrss3 49b4fab1ca 增加代码健壮性保护 2026-08-18 14:00:28 +08:00
6 changed files with 31 additions and 10 deletions
+1
View File
@@ -1 +1,2 @@
.trae/
.recode/
@@ -40,7 +40,7 @@ cell/font/ {
size = 14:System.Int32 @Desc:默认字体大小
alignment = MiddleCenter:UnityEngine.TextAnchor @Desc:默认文本对齐
richText = true:System.Boolean @Desc:是否启用富文本
fontAsset = guid:68dace674a028544187e7ffe55cdae8b:TMPro.TMP_FontAsset:TMPro.TMP_FontAsset @Desc:字体资源 @ResourceSoftReference:Packages/com.lrss3.xericuiactionvessel/Runtime/Font/SourceHanSansSC-Regular-2 SDF.asset
#fontAsset = guid:68dace674a028544187e7ffe55cdae8b:TMPro.TMP_FontAsset:TMPro.TMP_FontAsset @Desc:字体资源 @ResourceSoftReference:Packages/com.lrss3.xericuiactionvessel/Runtime/Font/SourceHanSansSC-Regular-2 SDF.asset
}
cell/default/ {
@@ -33,7 +33,8 @@ namespace XericUI.XTable.Rendering.Component
{
if (!m_Dragging) return;
m_Dragging = false;
m_Table?.EndSelection();
// 用 Unity 判空而非 ?.:表格可能已被销毁(“假 null”)。
if (m_Table != null) m_Table.EndSelection();
}
}
}
@@ -17,6 +17,7 @@ namespace XericUI.XTable.Rendering.Component
public void Configure(XericUIActionTable table, int rowStart, int rowEnd, int colStart, int colEnd)
{
if (table == null || this == null) return;
m_Table = table;
m_RowStart = rowStart;
m_RowEnd = rowEnd;
@@ -35,7 +36,11 @@ namespace XericUI.XTable.Rendering.Component
public void RefreshVisual()
{
// 防御:覆盖层自身可能已被销毁(“假 null”),此处的 Unity == 判空直接拦截,
// 防止继续走到 GetComponent 原生层抛 NullReferenceException。
if (this == null) return;
EnsureVisuals();
if (m_Fill == null) return;
if (m_Table == null || !m_Table.SelectionHandle.HasSelection || m_RowStart >= m_RowEnd || m_ColStart >= m_ColEnd)
{
m_Fill.gameObject.SetActive(false);
@@ -63,7 +68,11 @@ namespace XericUI.XTable.Rendering.Component
private void EnsureVisuals()
{
if (this == null) return;
// Image 组件理论上由 RequireComponent 保证,但外部代码可能移除它;
// GetComponent 缺失时返回 null,直接补一个,避免 input.color 空引用。
var input = GetComponent<Image>();
if (input == null) input = gameObject.AddComponent<Image>();
input.color = Color.clear;
input.raycastTarget = false;
for (int i = transform.childCount - 1; i >= 0; i--)
@@ -171,7 +171,10 @@ namespace XericUI.XTable.Rendering.Component
private XTableSelectionOverlay CreateOrUpdateSelectionOverlay(Transform parent, XTableSelectionOverlay overlay,
int rowStart, int rowEnd, int colStart, int colEnd, string name)
{
if (parent == null) return overlay;
// 父容器已销毁:旧引用是“假 null”,直接返回 null 让调用方清空字段,
// 避免把已销毁引用写回字段后在 RefreshSelectionOverlays 中崩溃。
if (parent == null) return null;
// Unity 的 == 能识别已销毁覆盖层的“假 null”,此处会正确重建。
if (overlay == null)
{
var go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(UnityEngine.UI.Image), typeof(XTableSelectionOverlay));
@@ -179,6 +182,7 @@ namespace XericUI.XTable.Rendering.Component
go.transform.SetParent(parent, false);
overlay = go.GetComponent<XTableSelectionOverlay>();
}
if (overlay == null) return null;
// Overlay 必须处于当前区域最上层,才能先于边框和其他 Graphic 接收 PointerDown/Drag。
overlay.Configure(this, rowStart, rowEnd, colStart, colEnd);
overlay.transform.SetAsLastSibling();
@@ -187,10 +191,12 @@ namespace XericUI.XTable.Rendering.Component
private void RefreshSelectionOverlays()
{
m_NormalSelectionOverlay?.RefreshVisual();
m_FrozenColumnSelectionOverlay?.RefreshVisual();
m_FrozenRowSelectionOverlay?.RefreshVisual();
m_FrozenCornerSelectionOverlay?.RefreshVisual();
// 注意:不能用 ?. —— C# 的 ?. 只检查托管 null,识别不了 Unity 已销毁对象的“假 null”;
// 对已销毁的覆盖层调用 RefreshVisual 会在 GetComponent 原生层抛 NullReferenceException。
if (m_NormalSelectionOverlay != null) m_NormalSelectionOverlay.RefreshVisual();
if (m_FrozenColumnSelectionOverlay != null) m_FrozenColumnSelectionOverlay.RefreshVisual();
if (m_FrozenRowSelectionOverlay != null) m_FrozenRowSelectionOverlay.RefreshVisual();
if (m_FrozenCornerSelectionOverlay != null) m_FrozenCornerSelectionOverlay.RefreshVisual();
}
private void SyncFrozenContainers()
@@ -205,8 +211,11 @@ namespace XericUI.XTable.Rendering.Component
private void SyncFrozenContainer(RectTransform container, Vector2 position)
{
if (container == null) return;
container.anchoredPosition = position;
container.sizeDelta = new Vector2(m_TotalWidth, m_TotalHeight);
// 偏移 m_ContentOffset(边框半宽)以对齐 Content 中单元格的偏移,
// 避免冻结区域左侧/顶部的 1px 边框因 0.5px 坐标差被 viewport 裁切。
float pad = m_ContentOffset * 2f;
container.anchoredPosition = position + new Vector2(m_ContentOffset, -m_ContentOffset);
container.sizeDelta = new Vector2(m_TotalWidth + pad, m_TotalHeight + pad);
}
}
}
@@ -106,7 +106,8 @@ namespace XericUI.XTable.Rendering.Component
{
if (m_Disposed) return;
m_Disposed = true;
m_Table?.EndCheckUpdate(m_Token);
// 用 Unity 判空而非 ?.:表格可能已被外部销毁(“假 null”),?. 仍会调用其方法。
if (m_Table != null) m_Table.EndCheckUpdate(m_Token);
m_Table = null;
}
}