From 2e91832f3956a679b6a23b1e4d153ccf6db5e88e Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Fri, 26 Jun 2026 23:27:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=B1=E4=BA=8Etabledata?= =?UTF-8?q?=E5=BF=98=E4=BA=86=E6=A0=87=E8=AE=B0=E5=8F=AF=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=AF=BC=E8=87=B4=E6=95=B0=E6=8D=AE=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E4=B8=BAnull=E5=AF=BC=E8=87=B4=E6=8A=A5=E9=94=99=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E3=80=82=20=E6=B7=BB=E5=8A=A0=E5=BF=AB?= =?UTF-8?q?=E6=8D=B7=E9=94=AE=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XTable/XericUIActionTableSceneEditor.cs | 80 ++ .../Test/xuiSceen.unity | 808 +++++++++++++++++- .../Rendering/Component/XericUIActionTable.cs | 101 ++- 3 files changed, 949 insertions(+), 40 deletions(-) diff --git a/Editor/XTable/XericUIActionTableSceneEditor.cs b/Editor/XTable/XericUIActionTableSceneEditor.cs index a1bf9ef..c16eac4 100644 --- a/Editor/XTable/XericUIActionTableSceneEditor.cs +++ b/Editor/XTable/XericUIActionTableSceneEditor.cs @@ -105,6 +105,7 @@ namespace XericUIEditor.XTable DrawSelectionHighlight(rh, cw, tableRect, xScale, yScale, table); DrawInputBar(table, tableRect.x, tableRect.y + tableRect.height + MARGIN); HandleInput(table, rh, cw, tableRect, xScale, yScale); + HandleKeyboard(table, rh, cw); Handles.EndGUI(); } @@ -276,6 +277,16 @@ namespace XericUIEditor.XTable margin = new RectOffset(2, 2, 2, 2) }; Rect inputRect = new Rect(barRect.x + 56, barRect.y + 2, 180, barRect.height - 4); + + // 回车键提交 + if (Event.current.isKey && Event.current.keyCode == KeyCode.Return + && GUI.GetNameOfFocusedControl() == "XTableSceneEditInput") + { + ApplyEdit(table); + Event.current.Use(); + } + + GUI.SetNextControlName("XTableSceneEditInput"); s_EditText = GUI.TextField(inputRect, s_EditText, inputStyle); var btnStyle = new GUIStyle(GUI.skin.button) @@ -287,6 +298,7 @@ namespace XericUIEditor.XTable if (GUI.Button(new Rect(barRect.x + 242, barRect.y + 2, 50, barRect.height - 4), "应用", btnStyle)) { + GUI.FocusControl(null); ApplyEdit(table); } } @@ -345,6 +357,74 @@ namespace XericUIEditor.XTable #endregion + #region 键盘快捷键 + + private static void HandleKeyboard(XericUIActionTable table, float[] rh, float[] cw) + { + Event e = Event.current; + if (e.type != EventType.KeyDown) return; + + // 输入框获得焦点时不拦截方向键(用于文本光标移动) + if (GUI.GetNameOfFocusedControl() == "XTableSceneEditInput") + return; + + int row = table.SelectedRow; + int col = table.SelectedCol; + bool shift = e.shift; + + switch (e.keyCode) + { + case KeyCode.UpArrow: + if (!TryClampToValid(row - 1, col, rh, cw, out row, out col)) return; + break; + case KeyCode.DownArrow: + if (!TryClampToValid(row + 1, col, rh, cw, out row, out col)) return; + break; + case KeyCode.LeftArrow: + if (!TryClampToValid(row, col - 1, rh, cw, out row, out col)) return; + break; + case KeyCode.RightArrow: + if (!TryClampToValid(row, col + 1, rh, cw, out row, out col)) return; + break; + default: + return; + } + + if (shift) + { + // Shift: 扩展/开始多选 + if (!table.HasMultiSelection && table.SelectedRow >= 0 && table.SelectedCol >= 0) + table.StartMultiSelect(table.SelectedRow, table.SelectedCol); + table.ExtendMultiSelect(row, col); + } + else + { + table.SelectCell(row, col); + } + + UpdateEditText(table, row, col); + table.RefreshView(); + e.Use(); + SceneView.RepaintAll(); + } + + /// 将行列约束到有效范围,若已到边界则返回 false(不移动) + private static bool TryClampToValid(int row, int col, float[] rh, float[] cw, + out int outRow, out int outCol) + { + if (row < 0 || row >= rh.Length || col < 0 || col >= cw.Length) + { + outRow = row; + outCol = col; + return false; + } + outRow = row; + outCol = col; + return true; + } + + #endregion + private static int IndexFromCoord(float coord, float[] sizes) { float acc = 0; diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Test/xuiSceen.unity b/Runtime/InterfaceAttributeReflectionGenerator/Test/xuiSceen.unity index 5b1d595..e62a9ad 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Test/xuiSceen.unity +++ b/Runtime/InterfaceAttributeReflectionGenerator/Test/xuiSceen.unity @@ -122,7 +122,7 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} ---- !u!1 &427173231 +--- !u!1 &176805029 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -130,63 +130,376 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 427173232} - - component: {fileID: 427173233} - m_Layer: 5 - m_Name: GameObject + - component: {fileID: 176805030} + - component: {fileID: 176805031} + - component: {fileID: 176805033} + - component: {fileID: 176805032} + m_Layer: 0 + m_Name: Xeric UI Action Table m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &427173232 +--- !u!224 &176805030 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 427173231} + m_GameObject: {fileID: 176805029} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 1607234778} + - {fileID: 759304923} + - {fileID: 1586214056} m_Father: {fileID: 1305945111} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 386, y: 0} - m_SizeDelta: {x: 424.7212, y: 503.0022} + m_AnchoredPosition: {x: 110.4, y: -224.9} + m_SizeDelta: {x: 499.2139, y: 296.9379} m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &427173233 +--- !u!114 &176805031 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 427173231} + m_GameObject: {fileID: 176805029} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} m_Name: m_EditorClassIdentifier: - m_Padding: - m_Left: 0 - m_Right: 0 - m_Top: 0 - m_Bottom: 0 - m_ChildAlignment: 0 - m_StartCorner: 0 - m_StartAxis: 0 - m_CellSize: {x: 100, y: 100} - m_Spacing: {x: 0, y: 0} - m_Constraint: 0 - m_ConstraintCount: 2 + m_Content: {fileID: 779072858} + m_Horizontal: 1 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 1 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 1 + m_Viewport: {fileID: 1607234778} + m_HorizontalScrollbar: {fileID: 759304926} + m_VerticalScrollbar: {fileID: 1586214059} + m_HorizontalScrollbarVisibility: 1 + m_VerticalScrollbarVisibility: 1 + m_HorizontalScrollbarSpacing: 0 + m_VerticalScrollbarSpacing: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &176805032 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 176805029} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.1, g: 0.1, b: 0.1, a: 0.3} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &176805033 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 176805029} + m_CullTransparentMesh: 1 --- !u!224 &473756093 stripped RectTransform: m_CorrespondingSourceObject: {fileID: 7740888090637087881, guid: 4f25b16125eac234e820506fff28a4b0, type: 3} m_PrefabInstance: {fileID: 2652053894482773542} m_PrefabAsset: {fileID: 0} +--- !u!1 &524897086 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 524897087} + m_Layer: 0 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &524897087 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 524897086} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2007027292} + m_Father: {fileID: 759304923} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &581047150 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 581047151} + m_Layer: 0 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &581047151 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 581047150} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1175835292} + m_Father: {fileID: 1586214056} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &759304922 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 759304923} + - component: {fileID: 759304926} + - component: {fileID: 759304925} + - component: {fileID: 759304924} + m_Layer: 0 + m_Name: Scrollbar Horizontal + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &759304923 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 759304922} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 524897087} + m_Father: {fileID: 176805030} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 0, y: 4} + m_SizeDelta: {x: -16, y: 20} + m_Pivot: {x: 0.5, y: 0} +--- !u!114 &759304924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 759304922} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.15, g: 0.15, b: 0.15, a: 0.5} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &759304925 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 759304922} + m_CullTransparentMesh: 1 +--- !u!114 &759304926 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 759304922} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 0} + m_HandleRect: {fileID: 2007027292} + m_Direction: 0 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &779072857 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 779072858} + - component: {fileID: 779072860} + - component: {fileID: 779072859} + m_Layer: 0 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &779072858 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 779072857} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1607234778} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 300, y: 70} + m_Pivot: {x: 0, y: 1} +--- !u!114 &779072859 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 779072857} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 13944afb118d5de499192b64114ec4f2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RowHeights: + - 40 + - 30 + m_ColWidths: + - 100 + - 80 + - 120 + m_DefaultStyleNamespace: xeric_table_default + m_ScrollRect: {fileID: 176805031} +--- !u!222 &779072860 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 779072857} + m_CullTransparentMesh: 1 --- !u!224 &879192284 stripped RectTransform: m_CorrespondingSourceObject: {fileID: 3719551361696879033, guid: 1715f06fee075eb45b68d43575e9aa88, type: 3} @@ -197,6 +510,81 @@ RectTransform: m_CorrespondingSourceObject: {fileID: 788574964665247507, guid: 0dc894baa18f5cb4eb9c82fd3c40d2bd, type: 3} m_PrefabInstance: {fileID: 5908854057788947888} m_PrefabAsset: {fileID: 0} +--- !u!1 &1175835291 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1175835292} + - component: {fileID: 1175835294} + - component: {fileID: 1175835293} + m_Layer: 0 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1175835292 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1175835291} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 581047151} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1175835293 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1175835291} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.4, g: 0.4, b: 0.4, a: 0.8} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1175835294 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1175835291} + m_CullTransparentMesh: 1 --- !u!1 &1189569455 GameObject: m_ObjectHideFlags: 0 @@ -289,6 +677,38 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1227271787 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1227271788} + m_Layer: 0 + m_Name: '[SingleInstance]' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1227271788 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1227271787} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1830323039} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1305945107 GameObject: m_ObjectHideFlags: 0 @@ -383,8 +803,8 @@ RectTransform: m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 427173232} - {fileID: 1376882762} + - {fileID: 176805030} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -482,11 +902,271 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _currentTheme: Default +--- !u!1 &1586214055 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1586214056} + - component: {fileID: 1586214059} + - component: {fileID: 1586214058} + - component: {fileID: 1586214057} + m_Layer: 0 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1586214056 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586214055} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 581047151} + m_Father: {fileID: 176805030} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -4, y: 0} + m_SizeDelta: {x: 20, y: -16} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &1586214057 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586214055} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.15, g: 0.15, b: 0.15, a: 0.5} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1586214058 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586214055} + m_CullTransparentMesh: 1 +--- !u!114 &1586214059 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586214055} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 0} + m_HandleRect: {fileID: 1175835292} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &1607234777 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1607234778} + - component: {fileID: 1607234781} + - component: {fileID: 1607234780} + - component: {fileID: 1607234779} + m_Layer: 0 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1607234778 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607234777} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 779072858} + m_Father: {fileID: 176805030} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1607234779 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607234777} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 1 +--- !u!114 &1607234780 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607234777} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.01} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1607234781 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1607234777} + m_CullTransparentMesh: 1 --- !u!224 &1802681382 stripped RectTransform: m_CorrespondingSourceObject: {fileID: 496212968317797178, guid: a58d335e7df1a994d9f1c0fd58aff68f, type: 3} m_PrefabInstance: {fileID: 2478084580577702765} m_PrefabAsset: {fileID: 0} +--- !u!1 &1830323038 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1830323039} + - component: {fileID: 1830323040} + m_Layer: 0 + m_Name: '[XericLifeCycleCore-OnlGlbInsImn]' + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1830323039 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1830323038} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1227271788} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1830323040 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1830323038} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 682102342, guid: 9bf3ac4b721466c4c99ca1ff9ff19f35, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1836412398 GameObject: m_ObjectHideFlags: 0 @@ -678,6 +1358,81 @@ RectTransform: m_CorrespondingSourceObject: {fileID: 101967088653580022, guid: f601d441cf135fe479d319c5eb01abd8, type: 3} m_PrefabInstance: {fileID: 1170375175641943129} m_PrefabAsset: {fileID: 0} +--- !u!1 &2007027291 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2007027292} + - component: {fileID: 2007027294} + - component: {fileID: 2007027293} + m_Layer: 0 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2007027292 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2007027291} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 524897087} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2007027293 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2007027291} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.4, g: 0.4, b: 0.4, a: 0.8} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2007027294 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2007027291} + m_CullTransparentMesh: 1 --- !u!1 &2007728709 GameObject: m_ObjectHideFlags: 0 @@ -1259,3 +2014,4 @@ SceneRoots: - {fileID: 1836412401} - {fileID: 1305945111} - {fileID: 1920416618} + - {fileID: 1227271788} diff --git a/Runtime/XTable/Rendering/Component/XericUIActionTable.cs b/Runtime/XTable/Rendering/Component/XericUIActionTable.cs index f82c54f..76fcdac 100644 --- a/Runtime/XTable/Rendering/Component/XericUIActionTable.cs +++ b/Runtime/XTable/Rendering/Component/XericUIActionTable.cs @@ -50,6 +50,13 @@ namespace XericUI.XTable.Rendering.Component private int m_SelectedRow = -1; private int m_SelectedCol = -1; + + // 多选范围 + private int m_SelectStartRow = -1; + private int m_SelectStartCol = -1; + private int m_SelectEndRow = -1; + private int m_SelectEndCol = -1; + [System.NonSerialized] private GameObject m_BackgroundGo; private float m_TotalWidth; @@ -71,7 +78,12 @@ namespace XericUI.XTable.Rendering.Component public XTableData TableData { - get => m_TableData; + get + { + if (m_TableData == null) + m_TableData = new XTableData(); + return m_TableData; + } set { m_TableData = value; MarkDirty(TableDirtyType.DataChanged); } } @@ -82,6 +94,23 @@ namespace XericUI.XTable.Rendering.Component public int SelectedRow => m_SelectedRow; public int SelectedCol => m_SelectedCol; + public int SelectStartRow => m_SelectStartRow; + public int SelectStartCol => m_SelectStartCol; + public int SelectEndRow => m_SelectEndRow; + public int SelectEndCol => m_SelectEndCol; + public bool HasMultiSelection => m_SelectStartRow >= 0 && m_SelectEndRow >= 0; + + /// 判断单元格是否在多选范围内 + public bool IsInSelectionRange(int row, int col) + { + if (!HasMultiSelection) return false; + int minR = Mathf.Min(m_SelectStartRow, m_SelectEndRow); + int maxR = Mathf.Max(m_SelectStartRow, m_SelectEndRow); + int minC = Mathf.Min(m_SelectStartCol, m_SelectEndCol); + int maxC = Mathf.Max(m_SelectStartCol, m_SelectEndCol); + return row >= minR && row <= maxR && col >= minC && col <= maxC; + } + #endregion #region 生命周期 @@ -148,7 +177,7 @@ namespace XericUI.XTable.Rendering.Component #region 默认示例数据 - private bool m_DefaultDataCreated; + [System.NonSerialized] private bool m_DefaultDataCreated; private void CreateDefaultSampleData() { @@ -168,11 +197,11 @@ namespace XericUI.XTable.Rendering.Component private void SetCellTextInternal(int row, int col, string text, bool skipDirty) { - var data = m_TableData.GetCell(row, col); + var data = TableData.GetCell(row, col); if (data == null) { data = new XTableCellData(); - m_TableData.SetCell(row, col, data); + TableData.SetCell(row, col, data); } data.Text = text; if (!skipDirty) MarkDirty(TableDirtyType.DataChanged); @@ -221,7 +250,7 @@ namespace XericUI.XTable.Rendering.Component public void SetCellData(int row, int col, XTableCellData data) { - m_TableData.SetCell(row, col, data); + TableData.SetCell(row, col, data); MarkDirty(TableDirtyType.DataChanged); } @@ -231,37 +260,70 @@ namespace XericUI.XTable.Rendering.Component } public XTableCellData GetCellData(int row, int col) - => m_TableData?.GetCell(row, col); + => TableData.GetCell(row, col); public void MergeCells(int startRow, int startCol, int rowSpan, int colSpan) { - m_TableData.MergeCells(startRow, startCol, rowSpan, colSpan); + TableData.MergeCells(startRow, startCol, rowSpan, colSpan); MarkDirty(TableDirtyType.DataChanged); } public void UnmergeCells(int startRow, int startCol) { - m_TableData.UnmergeCells(startRow, startCol); + TableData.UnmergeCells(startRow, startCol); MarkDirty(TableDirtyType.DataChanged); } public void SelectCell(int row, int col) { + ClearMultiSelection(); m_SelectedRow = row; m_SelectedCol = col; - MarkDirty(TableDirtyType.DataChanged); + MarkDirty(TableDirtyType.SelectionChanged); } public void ClearSelection() { m_SelectedRow = -1; m_SelectedCol = -1; - MarkDirty(TableDirtyType.DataChanged); + ClearMultiSelection(); + MarkDirty(TableDirtyType.SelectionChanged); + } + + /// 开始多选(以当前格为锚点) + public void StartMultiSelect(int row, int col) + { + m_SelectStartRow = row; + m_SelectStartCol = col; + m_SelectEndRow = row; + m_SelectEndCol = col; + m_SelectedRow = row; + m_SelectedCol = col; + MarkDirty(TableDirtyType.SelectionChanged); + } + + /// 扩展多选范围到目标格 + public void ExtendMultiSelect(int row, int col) + { + m_SelectEndRow = row; + m_SelectEndCol = col; + m_SelectedRow = row; + m_SelectedCol = col; + MarkDirty(TableDirtyType.SelectionChanged); + } + + /// 清除多选范围 + public void ClearMultiSelection() + { + m_SelectStartRow = -1; + m_SelectStartCol = -1; + m_SelectEndRow = -1; + m_SelectEndCol = -1; } public void RefreshView() { - if (m_TableData == null || m_Assembler == null) return; + if (m_Assembler == null) return; if (m_RowHeights == null || m_ColWidths == null) return; if (m_RowHeights.Length == 0 || m_ColWidths.Length == 0) return; @@ -354,6 +416,8 @@ namespace XericUI.XTable.Rendering.Component Color fgColor = GetStyleColor(m_DefaultStyleNamespace, "/cell/fg/color", new Color(0.05f, 0.05f, 0.05f)); Color selectionColor = new Color(0.18f, 0.42f, 0.82f, 0.25f); + Color multiSelectColor = new Color(0.18f, 0.42f, 0.82f, 0.12f); + Color focusColor = new Color(0.22f, 0.55f, 0.95f, 0.35f); int fontSize = GetStyleInt(m_DefaultStyleNamespace, "/cell/font/size", 14); for (int r = sr; r < er && r < m_RowHeights.Length; r++) @@ -364,10 +428,19 @@ namespace XericUI.XTable.Rendering.Component float cellY = -GetRowTopY(r); float cellW = m_ColWidths[c]; float cellH = m_RowHeights[r]; - XTableCellData data = m_TableData.GetCell(r, c); + XTableCellData data = TableData.GetCell(r, c); - // 选中高亮背景 - if (r == m_SelectedRow && c == m_SelectedCol) + // 多选范围高亮(覆盖被选中范围内的所有格) + if (IsInSelectionRange(r, c)) + { + var selImg = m_Assembler.GetImage("cell_multiselect"); + selImg.Rect.anchoredPosition = new Vector2(cellX, cellY); + selImg.Rect.sizeDelta = new Vector2(cellW, cellH); + selImg.Image.color = (r == m_SelectedRow && c == m_SelectedCol) + ? focusColor : multiSelectColor; + } + // 单选高亮 + else if (r == m_SelectedRow && c == m_SelectedCol) { var selImg = m_Assembler.GetImage("cell_select"); selImg.Rect.anchoredPosition = new Vector2(cellX, cellY);