From 08df172165c950c00241796f91e08f55187a454a Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sat, 27 Jun 2026 19:08:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A1=A8=E6=A0=BC=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=A1=8C=E5=88=97=E5=88=A0=E9=99=A4=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XTable/XericUIActionTableSceneEditor.cs | 114 +++++++++++------- .../Rendering/Component/XericUIActionTable.cs | 104 ++++++++++++++++ 2 files changed, 174 insertions(+), 44 deletions(-) diff --git a/Editor/XTable/XericUIActionTableSceneEditor.cs b/Editor/XTable/XericUIActionTableSceneEditor.cs index ad8e609..a1fc673 100644 --- a/Editor/XTable/XericUIActionTableSceneEditor.cs +++ b/Editor/XTable/XericUIActionTableSceneEditor.cs @@ -540,8 +540,11 @@ namespace XericUIEditor.XTable private const float BUBBLE_W = 60; private const float BUBBLE_H = 22; private const float BUBBLE_APPLY_BTN_W = 24; + private const float BUBBLE_DEL_BTN_W = 24; private static GUIStyle s_BubbleApplyBtnStyle; + private static GUIStyle s_BubbleApplyBtnStyleActive; + private static GUIStyle s_BubbleDelBtnStyle; private static void EnsureBubbleStyles() { @@ -550,14 +553,26 @@ namespace XericUIEditor.XTable { fontSize = 11, fontStyle = FontStyle.Bold, - normal = { textColor = Color.white, background = MakeTex(1, 1, s_ApplyBtnColor) }, - hover = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.2f, 0.55f, 0.2f, 0.95f)) }, - active = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.1f, 0.4f, 0.1f, 0.95f)) }, + normal = { textColor = new Color(0.5f, 0.5f, 0.5f), background = MakeTex(1, 1, new Color(0.25f, 0.25f, 0.25f, 0.6f)) }, + hover = { textColor = new Color(0.7f, 0.7f, 0.7f), background = MakeTex(1, 1, new Color(0.35f, 0.35f, 0.35f, 0.8f)) }, + active = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.15f, 0.15f, 0.15f, 0.8f)) }, padding = new RectOffset(0, 0, 0, 0), margin = new RectOffset(0, 0, 0, 0), alignment = TextAnchor.MiddleCenter, border = new RectOffset(0, 0, 0, 0) }; + s_BubbleApplyBtnStyleActive = new GUIStyle(s_BubbleApplyBtnStyle) + { + normal = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.2f, 0.6f, 0.2f, 0.9f)) }, + hover = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.2f, 0.55f, 0.2f, 0.95f)) }, + active = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.1f, 0.4f, 0.1f, 0.95f)) } + }; + s_BubbleDelBtnStyle = new GUIStyle(s_BubbleApplyBtnStyle) + { + normal = { textColor = new Color(0.6f, 0.3f, 0.3f), background = MakeTex(1, 1, new Color(0.3f, 0.15f, 0.15f, 0.6f)) }, + hover = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.7f, 0.2f, 0.2f, 0.95f)) }, + active = { textColor = Color.white, background = MakeTex(1, 1, new Color(0.5f, 0.1f, 0.1f, 0.95f)) } + }; } private static void DrawSizeEditBubble(float[] rh, float[] cw, Rect tr, @@ -566,64 +581,65 @@ namespace XericUIEditor.XTable int selRow = table.SelectedRow; int selCol = table.SelectedCol; - // 列宽气泡 + // 气泡固定总宽:输入框 + 应用按钮 + 删除按钮 + 间距 + const float totalW = BUBBLE_W + BUBBLE_APPLY_BTN_W + BUBBLE_DEL_BTN_W + 4f; + + // ── 列宽气泡:左对齐列左边缘,位于列标签上方 ── if (selCol >= 0 && selCol < cw.Length && selRow < 0) { float cx = tr.x + SumBefore(cw, selCol) * xScale; - float cwScaled = cw[selCol] * xScale; - float bubbleX = cx + cwScaled / 2f - BUBBLE_W / 2f; - float bubbleY = tr.y - ROW_H - MARGIN - 28f; - DrawBubble(table, bubbleX, bubbleY, cw[selCol], isColumn: true, index: selCol); + float bx = cx + 2f; + float by = tr.y - ROW_H - MARGIN - BUBBLE_H - 4f; + DrawBubble(table, bx, by, cw[selCol], totalW, isColumn: true, index: selCol); } + // ── 行高气泡:左对齐行标签左侧 ── if (selRow >= 0 && selRow < rh.Length && selCol < 0) { float ry = tr.y + SumBefore(rh, selRow) * yScale; float rhScaled = rh[selRow] * yScale; - float bubbleX = tr.x - COL_W - MARGIN - 86f; - float bubbleY = ry + rhScaled / 2f - 12f; - DrawBubble(table, bubbleX, bubbleY, rh[selRow], isColumn: false, index: selRow); + float bx = tr.x - COL_W - MARGIN - totalW - 4f; + float by = ry + (rhScaled - BUBBLE_H) / 2f; + DrawBubble(table, bx, by, rh[selRow], totalW, isColumn: false, index: selRow); } + // ── 单元格选中时同时显示 ── if (selRow >= 0 && selCol >= 0) { if (selCol < cw.Length) { float cx = tr.x + SumBefore(cw, selCol) * xScale; - float cwScaled = cw[selCol] * xScale; - float bubbleX = cx + cwScaled / 2f - BUBBLE_W / 2f; - float bubbleY = tr.y - ROW_H - MARGIN - 28f; - DrawBubble(table, bubbleX, bubbleY, cw[selCol], isColumn: true, index: selCol); + float bx = cx + 2f; + float by = tr.y - ROW_H - MARGIN - BUBBLE_H - 4f; + DrawBubble(table, bx, by, cw[selCol], totalW, isColumn: true, index: selCol); } if (selRow < rh.Length) { float ry = tr.y + SumBefore(rh, selRow) * yScale; float rhScaled = rh[selRow] * yScale; - float bubbleX = tr.x - COL_W - MARGIN - 86f; - float bubbleY = ry + rhScaled / 2f - 12f; - DrawBubble(table, bubbleX, bubbleY, rh[selRow], isColumn: false, index: selRow); + float bx = tr.x - COL_W - MARGIN - totalW - 4f; + float by = ry + (rhScaled - BUBBLE_H) / 2f; + DrawBubble(table, bx, by, rh[selRow], totalW, isColumn: false, index: selRow); } } } private static void DrawBubble(XericUIActionTable table, float x, float y, float currentValue, - bool isColumn, int index) + float totalW, bool isColumn, int index) { string controlName = isColumn ? $"XTableColSizeEdit_{index}" : $"XTableRowSizeEdit_{index}"; bool isFocused = GUI.GetNameOfFocusedControl() == controlName; - // 气泡容器(含输入框 + 应用按钮) - float totalW = isFocused ? BUBBLE_W + BUBBLE_APPLY_BTN_W + 2f : BUBBLE_W; - Rect bubbleRect = new Rect(x - (isFocused ? (BUBBLE_APPLY_BTN_W + 2f) / 2f : 0), y, totalW, BUBBLE_H); + Rect bubbleRect = new Rect(x, y, totalW, BUBBLE_H); EditorGUI.DrawRect(bubbleRect, s_BubbleBg); - EditorGUI.DrawRect(new Rect(bubbleRect.x, bubbleRect.y, bubbleRect.width, 1), s_BubbleBorder); - EditorGUI.DrawRect(new Rect(bubbleRect.x, bubbleRect.y + BUBBLE_H - 1, bubbleRect.width, 1), s_BubbleBorder); - EditorGUI.DrawRect(new Rect(bubbleRect.x, bubbleRect.y, 1, BUBBLE_H), s_BubbleBorder); - EditorGUI.DrawRect(new Rect(bubbleRect.x + bubbleRect.width - 1, bubbleRect.y, 1, BUBBLE_H), s_BubbleBorder); + EditorGUI.DrawRect(new Rect(x, y, totalW, 1), s_BubbleBorder); + EditorGUI.DrawRect(new Rect(x, y + BUBBLE_H - 1, totalW, 1), s_BubbleBorder); + EditorGUI.DrawRect(new Rect(x, y, 1, BUBBLE_H), s_BubbleBorder); + EditorGUI.DrawRect(new Rect(x + totalW - 1, y, 1, BUBBLE_H), s_BubbleBorder); var inputStyle = new GUIStyle(GUI.skin.textField) { @@ -632,33 +648,31 @@ namespace XericUIEditor.XTable alignment = TextAnchor.MiddleCenter }; - Rect inputRect = new Rect(bubbleRect.x + 2, bubbleRect.y + 2, - BUBBLE_W - 4, BUBBLE_H - 4); - + // 输入框占左 + Rect inputRect = new Rect(x + 2, y + 2, BUBBLE_W - 4, BUBBLE_H - 4); GUI.SetNextControlName(controlName); if (isFocused) - { s_SizeEditText = GUI.TextField(inputRect, s_SizeEditText, inputStyle); - } else - { GUI.TextField(inputRect, currentValue.ToString("F0"), inputStyle); + + // 应用按钮 — 聚焦时绿色,否则灰色 + EnsureBubbleStyles(); + Rect applyBtnRect = new Rect(x + BUBBLE_W + 1f, y + 1, BUBBLE_APPLY_BTN_W, BUBBLE_H - 2); + var btnStyle = isFocused ? s_BubbleApplyBtnStyleActive : s_BubbleApplyBtnStyle; + + if (GUI.Button(applyBtnRect, "✓", btnStyle)) + { + ApplyBubbleEdit(table, isColumn, index); + GUI.FocusControl(null); } - // ── 应用按钮(GUI.Button 确保正确交互)── - if (isFocused) + // 删除按钮 — 删除当前行/列 + Rect delBtnRect = new Rect(x + BUBBLE_W + BUBBLE_APPLY_BTN_W + 2f, y + 1, BUBBLE_DEL_BTN_W, BUBBLE_H - 2); + if (GUI.Button(delBtnRect, "✕", s_BubbleDelBtnStyle)) { - EnsureBubbleStyles(); - Rect applyBtnRect = new Rect( - bubbleRect.x + BUBBLE_W + 2f, bubbleRect.y, - BUBBLE_APPLY_BTN_W, BUBBLE_H); - - if (GUI.Button(applyBtnRect, "✓", s_BubbleApplyBtnStyle)) - { - ApplyBubbleEdit(table, isColumn, index); - GUI.FocusControl(null); - } + ApplyBubbleDelete(table, isColumn, index); } // 回车提交 @@ -687,6 +701,18 @@ namespace XericUIEditor.XTable SceneView.RepaintAll(); } + private static void ApplyBubbleDelete(XericUIActionTable table, bool isColumn, int index) + { + if (isColumn) + table.RemoveColumn(index); + else + table.RemoveRow(index); + + GUI.FocusControl(null); + s_State = EditorState.TableNavigation; + SceneView.RepaintAll(); + } + #endregion #region 底部输入框 diff --git a/Runtime/XTable/Rendering/Component/XericUIActionTable.cs b/Runtime/XTable/Rendering/Component/XericUIActionTable.cs index f9c8cc2..32fe9d8 100644 --- a/Runtime/XTable/Rendering/Component/XericUIActionTable.cs +++ b/Runtime/XTable/Rendering/Component/XericUIActionTable.cs @@ -330,6 +330,110 @@ namespace XericUI.XTable.Rendering.Component #endif } + /// 删除指定行,单元格数据上移 + public void RemoveRow(int row) + { + if (row < 0 || row >= m_RowHeights.Length) return; +#if UNITY_EDITOR + UnityEditor.Undo.RecordObject(this, "Remove Table Row"); +#endif + int colCount = m_ColWidths.Length; + + // 先解除涉及该行的合并 + for (int c = 0; c < colCount; c++) + TableData.UnmergeCells(row, c); + + // 将下方行数据上移 + for (int r = row + 1; r < m_RowHeights.Length; r++) + for (int c = 0; c < colCount; c++) + { + var data = TableData.GetCell(r, c); + if (data != null) + TableData.SetCell(r - 1, c, data); + else + TableData.SetCell(r - 1, c, null); // 显式清空原位置 + } + + // 清空最后一行数据 + int lastRow = m_RowHeights.Length - 1; + for (int c = 0; c < colCount; c++) + TableData.SetCell(lastRow, c, null); + + // 缩小行高数组 + int newLen = m_RowHeights.Length - 1; + var newArr = new float[newLen]; + for (int i = 0; i < row; i++) + newArr[i] = m_RowHeights[i]; + for (int i = row; i < newLen; i++) + newArr[i] = m_RowHeights[i + 1]; + m_RowHeights = newArr; + + // 更新选中状态 + if (m_SelectedRow >= m_RowHeights.Length) + m_SelectedRow = Mathf.Max(-1, m_RowHeights.Length - 1); + if (m_SelectStartRow >= m_RowHeights.Length) + m_SelectStartRow = Mathf.Max(-1, m_RowHeights.Length - 1); + if (m_SelectEndRow >= m_RowHeights.Length) + m_SelectEndRow = Mathf.Max(-1, m_RowHeights.Length - 1); + + MarkDirty(TableDirtyType.LayoutChanged | TableDirtyType.DataChanged | TableDirtyType.SelectionChanged); +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + } + + /// 删除指定列,单元格数据左移 + public void RemoveColumn(int col) + { + if (col < 0 || col >= m_ColWidths.Length) return; +#if UNITY_EDITOR + UnityEditor.Undo.RecordObject(this, "Remove Table Column"); +#endif + int rowCount = m_RowHeights.Length; + + // 先解除涉及该列的合并 + for (int r = 0; r < rowCount; r++) + TableData.UnmergeCells(r, col); + + // 将右侧列数据左移 + for (int r = 0; r < rowCount; r++) + for (int c = col + 1; c < m_ColWidths.Length; c++) + { + var data = TableData.GetCell(r, c); + if (data != null) + TableData.SetCell(r, c - 1, data); + else + TableData.SetCell(r, c - 1, null); + } + + // 清空最后一列数据 + int lastCol = m_ColWidths.Length - 1; + for (int r = 0; r < rowCount; r++) + TableData.SetCell(r, lastCol, null); + + // 缩小列宽数组 + int newLen = m_ColWidths.Length - 1; + var newArr = new float[newLen]; + for (int i = 0; i < col; i++) + newArr[i] = m_ColWidths[i]; + for (int i = col; i < newLen; i++) + newArr[i] = m_ColWidths[i + 1]; + m_ColWidths = newArr; + + // 更新选中状态 + if (m_SelectedCol >= m_ColWidths.Length) + m_SelectedCol = Mathf.Max(-1, m_ColWidths.Length - 1); + if (m_SelectStartCol >= m_ColWidths.Length) + m_SelectStartCol = Mathf.Max(-1, m_ColWidths.Length - 1); + if (m_SelectEndCol >= m_ColWidths.Length) + m_SelectEndCol = Mathf.Max(-1, m_ColWidths.Length - 1); + + MarkDirty(TableDirtyType.LayoutChanged | TableDirtyType.DataChanged | TableDirtyType.SelectionChanged); +#if UNITY_EDITOR + UnityEditor.EditorUtility.SetDirty(this); +#endif + } + public void SelectCell(int row, int col) { ClearMultiSelection();