From 283a4442280b48ee40686f2335c0bef850460d8b Mon Sep 17 00:00:00 2001 From: LiRuoChen <571244399@qq.com> Date: Fri, 24 Apr 2026 19:42:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=8F=8D=E5=B0=84=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=95=8C=E9=9D=A2=E5=9F=BA=E6=9C=AC=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E3=80=82=20=E4=BF=AE=E5=A4=8D=E6=87=92=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=A1=86=E7=9A=84=E5=B8=83=E5=B1=80=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/Helper/GridLayoutCalculator.cs | 28 +- .../{Resource.meta => Resources.meta} | 0 .../Resources/Config.meta | 8 + .../{Resource => Resources}/Prefabs.meta | 0 .../{Resource => Resources}/Sprites.meta | 0 .../Sprites/borderGrey32-4.png | Bin .../Sprites/borderGrey32-4.png.meta | 0 .../Scripts/IXuiavrgAttribute.cs | 7 + .../Scripts/IXuiavrgAttribute.cs.meta | 3 + .../Scripts/XuiavrPropertyScriptableObject.cs | 107 ++++++ .../XuiavrPropertyScriptableObject.cs.meta | 3 + .../Scripts/XuiavrgAttribute.cs | 77 ++++- .../Scripts/XuiavrgHideAttribute.cs | 28 ++ .../Scripts/XuiavrgHideAttribute.cs.meta | 3 + .../Scripts/XuiavrgInspectorManager.cs | 119 +++++++ ...s.meta => XuiavrgInspectorManager.cs.meta} | 0 .../Scripts/XuiavrgInspectorProperty.cs | 142 ++++++++ .../Scripts/XuiavrgInspectorProperty.cs.meta | 3 + .../Scripts/XuiavrgLocator.cs | 305 ++++++++++++++++++ ...Locator.cs.meta => XuiavrgLocator.cs.meta} | 0 .../Scripts/XuiavrgManager.cs | 30 -- .../Scripts/XuiavrgPooledLocator.cs | 14 - .../XuiavrgPropertyGroupScriptableObject.cs | 77 +++++ ...iavrgPropertyGroupScriptableObject.cs.meta | 3 + .../Scripts/XuiavrgUtilities.cs | 19 +- Runtime/OverrideLayout/XericScrollRect.cs | 43 ++- 26 files changed, 953 insertions(+), 66 deletions(-) rename Runtime/InterfaceAttributeReflectionGenerator/{Resource.meta => Resources.meta} (100%) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Resources/Config.meta rename Runtime/InterfaceAttributeReflectionGenerator/{Resource => Resources}/Prefabs.meta (100%) rename Runtime/InterfaceAttributeReflectionGenerator/{Resource => Resources}/Sprites.meta (100%) rename Runtime/InterfaceAttributeReflectionGenerator/{Resource => Resources}/Sprites/borderGrey32-4.png (100%) rename Runtime/InterfaceAttributeReflectionGenerator/{Resource => Resources}/Sprites/borderGrey32-4.png.meta (100%) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs rename Runtime/InterfaceAttributeReflectionGenerator/Scripts/{XuiavrgManager.cs.meta => XuiavrgInspectorManager.cs.meta} (100%) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs rename Runtime/InterfaceAttributeReflectionGenerator/Scripts/{XuiavrgPooledLocator.cs.meta => XuiavrgLocator.cs.meta} (100%) delete mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs delete mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs.meta diff --git a/Runtime/Helper/GridLayoutCalculator.cs b/Runtime/Helper/GridLayoutCalculator.cs index 200ca0e..e1ac234 100644 --- a/Runtime/Helper/GridLayoutCalculator.cs +++ b/Runtime/Helper/GridLayoutCalculator.cs @@ -19,6 +19,17 @@ namespace XericUI.Helper /// /// 计算所有项目的布局 /// + /// 子项数量 + /// 所需尺寸 + /// 单元尺寸 + /// 单元空隙 + /// 外围空隙 + /// 起始角 + /// 起始轴 + /// 容器尺寸 + /// 需要的列数 + /// 子项对齐 + /// public static List CalculateGridLayout( List layoutItems, int childCount, @@ -143,13 +154,13 @@ namespace XericUI.Helper float baseOffset; if (axis == 0) // X轴 { - // 如果startCorner在右侧,基准点应该是padding.right + // 如果startCorner在右侧,基准点应该是availableSpace - padding.right - requiredSpace int cornerX = (int)startCorner % 2; baseOffset = (cornerX == 0) ? padding.left : (availableSpace - padding.right - requiredSpace); } else // Y轴 { - // 如果startCorner在底部,基准点应该是padding.bottom + // 如果startCorner在底部,基准点应该是availableSpace - padding.bottom - requiredSpace int cornerY = (int)startCorner / 2; baseOffset = (cornerY == 0) ? padding.top : (availableSpace - padding.bottom - requiredSpace); } @@ -176,10 +187,12 @@ namespace XericUI.Helper if (startAxis == GridLayoutGroup.Axis.Horizontal) { if (constraint == GridLayoutGroup.Constraint.FixedRowCount && - childCount - index <= childrenToMove) + childrenToMove > 0 && childCount - index <= childrenToMove) { + // 计算偏移量,确保gridY不会为负数 + int offset = childCount - index - 1; gridX = 0; - gridY = constraintCount - (childCount - index); + gridY = Mathf.Min(offset, constraintCount - 1); } else { @@ -190,9 +203,11 @@ namespace XericUI.Helper else { if (constraint == GridLayoutGroup.Constraint.FixedColumnCount && - childCount - index <= childrenToMove) + childrenToMove > 0 && childCount - index <= childrenToMove) { - gridX = constraintCount - (childCount - index); + // 计算偏移量,确保gridX不会为负数 + int offset = childCount - index - 1; + gridX = Mathf.Min(offset, constraintCount - 1); gridY = 0; } else @@ -201,7 +216,6 @@ namespace XericUI.Helper gridY = index % cellCountY; } } - } private static int CalculateChildrenToMove( diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resource.meta b/Runtime/InterfaceAttributeReflectionGenerator/Resources.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Resource.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Resources.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resources/Config.meta b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Config.meta new file mode 100644 index 0000000..af0ca65 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5661a6f69ad8f9e4a8085712f4ac7e38 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resource/Prefabs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Prefabs.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Resource/Prefabs.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Resources/Prefabs.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites.meta b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites/borderGrey32-4.png b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites/borderGrey32-4.png similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites/borderGrey32-4.png rename to Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites/borderGrey32-4.png diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites/borderGrey32-4.png.meta b/Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites/borderGrey32-4.png.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Resource/Sprites/borderGrey32-4.png.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Resources/Sprites/borderGrey32-4.png.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs new file mode 100644 index 0000000..44d9a08 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs @@ -0,0 +1,7 @@ +namespace XericUI.ReflectionUIGenerator +{ + public interface IXuiavrgAttribute + { + public void BindProperty(XuiavrgInspectorProperty property); + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs.meta new file mode 100644 index 0000000..15b99d6 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/IXuiavrgAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 998589d7b3414ddd99b69cb128d7e052 +timeCreated: 1777014404 \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs new file mode 100644 index 0000000..36a81ba --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector; +#endif +using UnityEngine; +using XericLibrary.Runtime.MacroLibrary; +using Object = UnityEngine.Object; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 属性映射器 + /// + [Serializable] + [CreateAssetMenu(fileName = "Xeric UI avr Property", menuName = "Xeric Library/UI Action Vessel/Reflection UI Gen/UI avr Property", order = 10)] + public class XuiavrPropertyScriptableObject : ScriptableObject + { +#if ODIN_INSPECTOR + [LabelText("属性类型")] + [ValueDropdown("AssemblyValueTypeNames")] +#endif + public string valueType; +#if ODIN_INSPECTOR + [LabelText("属性名称")] +#endif + public string propertyName; +#if ODIN_INSPECTOR + [LabelText("界面预制体")] +#endif + public GameObject prefab; + + /// + /// 属性类型 + /// + public System.Type ValueType + { + get + { + if (_valueType != null) + return _valueType; + return _valueType = Type.GetType(valueType); + } + } + private System.Type _valueType; + + /// + /// 属性类型名称枚举 + /// + private IEnumerable AssemblyValueTypeNames + { + get + { + if (_assemblyValueTypeName != null) + return _assemblyValueTypeName; + _assemblyValueTypeName = new ValueDropdownList(); + + // foreach (var typeName in MacroReflection.GetAllAssignableTypeInAppDomainAssembly( + // typeof(float), + // typeof(string), + // typeof(bool), + // typeof(Enum), + // typeof(int) + // ).Select(a => a.FullName)) + // { + // _assemblyValueTypeName.Add(typeName); + // } + + _assemblyValueTypeName.AddRange(AllValueType.Select(a => new ValueDropdownItem(a.Name, a.FullName))); + return _assemblyValueTypeName; + } + } + private static ValueDropdownList _assemblyValueTypeName = null; + + /// + /// 直接定义所有需要检查的类型 + /// + public static IEnumerable AllValueType + { + get + { + yield return typeof(Enum); + yield return typeof(decimal); + yield return typeof(double); + yield return typeof(float); + yield return typeof(ulong); + yield return typeof(long); + yield return typeof(int); + yield return typeof(uint); + yield return typeof(short); + yield return typeof(ushort); + yield return typeof(byte); + yield return typeof(sbyte); + yield return typeof(string); + yield return typeof(char); + yield return typeof(bool); + } + } + + + + + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs.meta new file mode 100644 index 0000000..fbe1215 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrPropertyScriptableObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: be0acbdac8e549578dcccf8c076a5433 +timeCreated: 1776998687 \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgAttribute.cs index d3972c6..80a349a 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgAttribute.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgAttribute.cs @@ -1,7 +1,78 @@ -namespace XericUI.ReflectionUIGenerator +using System; + +namespace XericUI.ReflectionUIGenerator { - public class XuiavrgAttribute + /// + /// 反射属性标记 + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class XuiavrgAttribute : Attribute, IXuiavrgAttribute { - + #region 字段属性 + + public readonly string LabelName; + public readonly string UIPropertyName; + public readonly string UnitLabelName; + public readonly bool MustFillIn; + public readonly int InspectorOrder; + public readonly string GroupName; + public readonly string OnTriggerCall; + public readonly XuiavrgSize ArgumentSize; + + /// + /// 标记属性 + /// + /// 属性显示名称 + /// 属性标记名(对应界面元素名称) + /// 属性显示单位名称 + /// 是否标记为必填项目 + /// 属性显示顺序 + /// 属性组名称(可以是以/&\分割的路径) + /// 属性附加的动作,调用此内联反射调用的函数 + /// 属性组名称(可以是以/&\分割的路径) + public XuiavrgAttribute(string labelName, string uiPropertyName, string unitLabelName, bool mustFillin, + int inspectorOrder = 0, string groupName = null, string onTriggerCall = "", XuiavrgSize size = XuiavrgSize.Normal) + { + LabelName = labelName; + UIPropertyName = uiPropertyName; + UnitLabelName = unitLabelName; + MustFillIn = mustFillin; + InspectorOrder = inspectorOrder; + GroupName = groupName; + OnTriggerCall = onTriggerCall; + ArgumentSize = size; + } + + #endregion + + #region 生成器 + + public void BindProperty(XuiavrgInspectorProperty property) + { + property.labelName = LabelName; + property.uiPropertyName = UIPropertyName; + property.unitLabelName = UnitLabelName; + property.mustFillIn = MustFillIn; + property.inspectorOrder = InspectorOrder; + property.groupName = GroupName; + property.onTriggerCall = OnTriggerCall; + property.argumentSize = ArgumentSize; + } + + #endregion + } + + /// + /// 属性尺寸 + /// + public enum XuiavrgSize + { + None = 0, + + Tiny = 1000 - 2, + Small = 1000 - 1, + Normal = 1000, + Big, + Large, } } \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs new file mode 100644 index 0000000..b74ca34 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs @@ -0,0 +1,28 @@ +using System; +using UnityEngine.Serialization; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 隐藏标记 + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class XuiavrgHideAttribute : Attribute, IXuiavrgAttribute + { + public readonly bool Hide; + + /// + /// 标记属性 + /// + /// 隐藏属性 + public XuiavrgHideAttribute(bool hideInInspector = true) + { + Hide = hideInInspector; + } + + public void BindProperty(XuiavrgInspectorProperty property) + { + property.hideInInspector = Hide; + } + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs.meta new file mode 100644 index 0000000..8e36c83 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgHideAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 12f7aecaa5224304b1058ab9dc04a95f +timeCreated: 1776995602 \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs new file mode 100644 index 0000000..d61fe21 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Deconstruction.Type.Peculiarity; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector; +#endif +using UnityEngine; +using UnityEngine.Pool; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 节点下生成反射属性管理器 + /// + /// + /// 管理器需要挂载在unity节点中,管理器中指定的属性承载节点将用于生成属性内容。 + /// + /// 为了准确生成界面属性,首先需要准备界面属性预制体。 + /// 在包含界面交互元素的预制体跟目录上挂载 脚本,其将作为组件入口,管理所有的交互组件,必须在脚本中添加所有允许交互的元素。 + /// (注:如果你没有odin插件,那么组件样式可能会与设计的不一致,请务必确保每个数组中只包含一个组件) + /// + /// 然后在工程中创建 对象,指定对应的数据类型,将刚刚创建的界面预制体拖入,这样就完成了两者的绑定。 + /// 为了方便管理,再创建一个 对象,它可以成为一个组,快速管理和切换所需的数据集合。 + /// + /// 接下来,在管理器中填入使用的 对象,然后调用 SchemaHydration 方法传入负载,界面顷刻生成。 + /// + /// + public class XuiavrgInspectorManager : MonoBehaviour + { + public static Func CreatInspectorProperty = () => new XuiavrgInspectorProperty(); + public static Action GetInspectorProperty = a => {}; + public static Action ReleaseInspectorProperty = a => a.Clear(); + public static Action DestoryInspectorProperty = a => {}; + public static ObjectPool InspectorPropertyPool + = new ObjectPool(CreatInspectorProperty, GetInspectorProperty, ReleaseInspectorProperty, DestoryInspectorProperty); + +#if ODIN_INSPECTOR + [LabelText("预制体组")] +#endif + public XuiavrgPropertyGroupScriptableObject propertyUIPrefabsGroup; +#if ODIN_INSPECTOR + [LabelText("反射属性预览")] +#endif + public List allProperty = new List(); + + + /// + /// 装填数据负载 + /// + /// 数据端对象 +#if ODIN_INSPECTOR + [Button] +#endif + public void SchemaHydration(object payload) + { + // 清空环境和界面 + allProperty.Clear(); + InspectorPropertyPool.Clear(); + + if (!propertyUIPrefabsGroup) + { + Debug.LogError("未指定编组,无法生成界面"); + return; + } + + if (payload != null) + { + Debug.LogError("未指定有效负载,无法生成界面"); + return; + } + + // 从反射构建 + var type = payload.GetType(); + SchemaHydration(allProperty, type, payload); + } + + + + /// + /// 状态数据负载 + /// + /// 反射属性结合 + /// 负载实例 + /// 进行检查的负载类型 + private static void SchemaHydration(List properties, object payload) + => SchemaHydration(properties, typeof(T), payload); + + /// + /// 装填数据负载 + /// + /// 反射属性集合 + /// 进行检查的负载类型 + /// 负载实例 + private static void SchemaHydration(List properties, Type payloadType, object payload) + { + bool needSort = false; + foreach (var memberInfo in payloadType.GetRuntimeMemberInfo()) + { + var mw = InspectorPropertyPool.Get(); + // 绑定负载 + mw.RegsiterDataValue(memberInfo, payload); + // 字段属性上的所有特性都进行标记 + foreach (var memberAttribute in memberInfo.GetCustomAttributes() + .OfType()) + { + memberAttribute.BindProperty(mw); + needSort |= mw.inspectorOrder != 0; // 如果有成员使用了order + } + properties.Add(mw); + } + // 排序 + if (needSort) + properties.Sort((property1, property2) => property1.inspectorOrder.CompareTo(property2.inspectorOrder)); + } + + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs new file mode 100644 index 0000000..972a807 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs @@ -0,0 +1,142 @@ +using System; +using System.Reflection; +using UnityEngine.Serialization; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 反射属性中间层 + /// + [Serializable] + public class XuiavrgInspectorProperty + { + public object valueTarget; + public bool isProperty; + public PropertyInfo propertyInfo; + public FieldInfo fieldInfo; + + public string labelName; + public string uiPropertyName; + public string unitLabelName; + public bool mustFillIn; + public int inspectorOrder; + public string groupName; + public string onTriggerCall; + public XuiavrgSize argumentSize; + public bool hideInInspector; + + + /// + /// 绑定到数据端 + /// + /// + /// + public void RegsiterDataValue(MemberInfo memberInfo, object target) + { + switch (memberInfo) + { + case PropertyInfo propertyInfo: + RegsiterDataValue(propertyInfo, target); + break; + case FieldInfo fieldInfo: + RegsiterDataValue(fieldInfo, target); + break; + } + } + + /// + /// 绑定到数据端 + /// + /// + /// + public virtual void RegsiterDataValue(PropertyInfo memberInfo, object target) + { + valueTarget = target; + isProperty = true; + propertyInfo = memberInfo; + } + + /// + /// 绑定到数据端 + /// + /// + /// + public virtual void RegsiterDataValue(FieldInfo memberInfo, object target) + { + valueTarget = target; + isProperty = false; + fieldInfo = memberInfo; + } + + /// + /// 绑定到界面端 + /// + public virtual void RegsiterUIElement() + { + + } + + + /// + /// 数据端值发生变更 + /// + public void CallDataValueChange() + { + + } + + /// + /// 界面端数据发生变更 + /// + public void CallUIValueChange() + { + + } + + /// + /// 从数据端获取值 + /// + /// + public object GetValueFromTargetMember() + { + if (isProperty) + return propertyInfo.GetValue(valueTarget); + else + return fieldInfo.GetValue(valueTarget); + } + + /// + /// 设置值到数据端 + /// + /// + public void SetValueToTargetMember(object value) + { + if (isProperty) + propertyInfo.SetValue(valueTarget, value); + else + fieldInfo.SetValue(valueTarget, value); + } + + /// + /// 从界面端获取值 + /// + /// + public object GetValueFormUIElement() + { + return null; + } + + /// + /// 设置值到界面端 + /// + /// + public void SetValueToUIElement(object value) + { + + } + + public void Clear() + { + } + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs.meta new file mode 100644 index 0000000..a6c88e1 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorProperty.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5d82ae110adb48b397c09939694cc3b2 +timeCreated: 1777011022 \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs new file mode 100644 index 0000000..b4f1b7b --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.Serialization; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// ui·´ÉäÊôÐԶ¨λÆ÷£¬·ÅÔÚUI¶ÔÏóµĸù½ڵãÉϣ¬É趨ºýÓÏÂÀ´µÄĿ±꼴¿É + /// + public class XuiavrgLocator : MonoBehaviour + { + public delegate void AnyTrigger(LocatorProperty locator, object value, int selectStart, int selectEnd); + +#if ODIN_INSPECTOR + [LabelText("ӳÉä×é¼þ")] +#endif + public List Properties; + + [Serializable] + public class LocatorProperty + { +#if ODIN_INSPECTOR + [LabelText("×é¼þ±êʶÃû")] +#endif + [Tooltip("½¨ÒéʹÓà name, label, unit, sign, input, output, button, slider, edit, select µȼòµ¥Ò׶®µÄÃû³ƽøÐбê¼Ç")] + public string propertyName; +#if ODIN_INSPECTOR + [LabelText("×é¼þÀàÐÍ")] +#endif + public UIElementClassify classify; +#if dUI_TextMeshPro +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.TMPText)] +#endif + public TMPro.TMP_Text TMPText; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.TMPInputField)] +#endif + public TMPro.TMP_InputField TMPInputField; +#endif +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIText)] +#endif + public UnityEngine.UI.Text UIText; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIInputField)] +#endif + public UnityEngine.UI.InputField UIInputField; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIToggle)] +#endif + public UnityEngine.UI.Toggle UIToggle; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIButton)] +#endif + public UnityEngine.UI.Button UIButton; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UISlider)] +#endif + public UnityEngine.UI.Slider UISlider; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIDropdown)] +#endif + public UnityEngine.UI.Dropdown UIDropdown; + +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.TMPInputField)] +#endif + public TMPInputTriggerEvent TMPInputTriggerOnEventType; +#if ODIN_INSPECTOR + [ShowIf("classify", UIElementClassify.UIInputField)] +#endif + public UIInputTriggerEvent UIInputTriggerOnEventType; + + /// + /// ֧³ֵÄ×é¼þÀàÐÍ + /// + public enum UIElementClassify + { + None, +#if dUI_TextMeshPro + TMPText, + TMPInputField, +#endif + UIText, + UIInputField, + UIToggle, + UIButton, + UISlider, + UIDropdown, + } + + [Flags] + public enum TMPInputTriggerEvent + { + none, + onEndEdit = 1 << 1, + onSubmit = 1 << 2, + onSelect = 1 << 3, + onDeselect = 1 << 4, + onTextSelection = 1 << 5, + onEndTextSelection = 1 << 6, + onValueChanged = 1 << 7, + onTouchScreenKeyboardStatusChanged = 1 << 8, + } + + public enum UIInputTriggerEvent + { + none, + onEndEdit, + onSubmit, + onValueChanged, + } + + /// + /// »ñȡֵ + /// + /// + /// + public object GetValue() + { + switch (classify) + { +#if dUI_TextMeshPro + case UIElementClassify.TMPText: + return TMPText.text; + case UIElementClassify.TMPInputField: + return TMPInputField.text; +#endif + case UIElementClassify.UIText: + return UIText.text; + case UIElementClassify.UIInputField: + return UIInputField.text; + case UIElementClassify.UIToggle: + return UIToggle.isOn; + case UIElementClassify.UIButton: + return false; + case UIElementClassify.UISlider: + return UISlider.value; + case UIElementClassify.UIDropdown: + return UIDropdown.value; + default: + return null; + } + } + + /// + /// ÉèÖÃֵ + /// + /// + /// + public void SetValue(object value) + { + switch (classify) + { +#if dUI_TextMeshPro + case UIElementClassify.TMPText: + TMPText.text = value.ToString(); + break; + case UIElementClassify.TMPInputField: + TMPInputField.text = value.ToString(); + break; +#endif + case UIElementClassify.UIText: + UIText.text = value.ToString(); + break; + case UIElementClassify.UIInputField: + UIInputField.text = value.ToString(); + break; + case UIElementClassify.UIToggle: + UIToggle.isOn = Convert.ToBoolean(value); + break; + case UIElementClassify.UIButton: + // UIButton do nothing + break; + case UIElementClassify.UISlider: + UISlider.value = Convert.ToSingle(value); + break; + case UIElementClassify.UIDropdown: + UIDropdown.value = Convert.ToInt32(value); + break; + default: + break; + } + } + + /// + /// ui´¥·¢Ê¼þ + /// + public event AnyTrigger OnUITrigger + { + add + { + switch (classify) + { +#if dUI_TextMeshPro + case UIElementClassify.TMPInputField: + switch (TMPInputTriggerOnEventType) + { + case TMPInputTriggerEvent.onEndEdit: + TMPInputField.onEndEdit.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case TMPInputTriggerEvent.onSubmit: + TMPInputField.onSubmit.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case TMPInputTriggerEvent.onSelect: + TMPInputField.onSelect.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case TMPInputTriggerEvent.onDeselect: + TMPInputField.onDeselect.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case TMPInputTriggerEvent.onTextSelection: + TMPInputField.onTextSelection.AddListener((content, start, end) => + value?.Invoke(this, content, start, end)); + break; + case TMPInputTriggerEvent.onEndTextSelection: + TMPInputField.onEndTextSelection.AddListener((content, start, end) => + value?.Invoke(this, content, start, end)); + break; + case TMPInputTriggerEvent.onValueChanged: + TMPInputField.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case TMPInputTriggerEvent.onTouchScreenKeyboardStatusChanged: + TMPInputField.onTouchScreenKeyboardStatusChanged.AddListener(a => + value?.Invoke(this, a, 0, 0)); + break; + default: + break; + } + + break; +#endif + case UIElementClassify.UIInputField: + switch (UIInputTriggerOnEventType) + { + case UIInputTriggerEvent.onEndEdit: + UIInputField.onEndEdit.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case UIInputTriggerEvent.onSubmit: + UIInputField.onSubmit.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case UIInputTriggerEvent.onValueChanged: + UIInputField.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + default: break; + } + + break; + case UIElementClassify.UIToggle: + UIToggle.onValueChanged.AddListener(a => value?.Invoke(this, value, 0, 0)); + break; + case UIElementClassify.UIButton: + UIButton.onClick.AddListener(() => value?.Invoke(this, true, 0, 0)); + break; + case UIElementClassify.UISlider: + UISlider.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + case UIElementClassify.UIDropdown: + UIDropdown.onValueChanged.AddListener(a => value?.Invoke(this, a, 0, 0)); + break; + default: + break; + } + } + remove { Debug.Log("ÈÎÒⴥ·¢Æ÷Î޷¨жÔØÌض¨Ê¼þ"); } + } + + /// + /// Çå¿Õui´¥·¢Ê¼þ + /// + public void ClearTriggerEvent() + { + if (TMPInputField) + { + TMPInputField.onEndEdit.RemoveAllListeners(); + TMPInputField.onSubmit.RemoveAllListeners(); + TMPInputField.onSelect.RemoveAllListeners(); + TMPInputField.onDeselect.RemoveAllListeners(); + TMPInputField.onTextSelection.RemoveAllListeners(); + TMPInputField.onEndTextSelection.RemoveAllListeners(); + TMPInputField.onValueChanged.RemoveAllListeners(); + TMPInputField.onTouchScreenKeyboardStatusChanged.RemoveAllListeners(); + } + + if (UIInputField) + { + UIInputField.onEndEdit.RemoveAllListeners(); + UIInputField.onSubmit.RemoveAllListeners(); + UIInputField.onValueChanged.RemoveAllListeners(); + } + + UIToggle?.onValueChanged.RemoveAllListeners(); + UIButton?.onClick.RemoveAllListeners(); + UISlider?.onValueChanged.RemoveAllListeners(); + UIDropdown?.onValueChanged.RemoveAllListeners(); + } + } + + // todo ʹÓÃÃû³ƣ¬ÀàÐÍɸѡµõ½×é¼þ + // todo ¸ù¾ÝÃû³Æ×齨×ֵäÊ÷¼ÓËٲéÕң¨»òÐí²»Óã© + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs.meta similarity index 100% rename from Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs.meta rename to Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgLocator.cs.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs deleted file mode 100644 index 3eb3be2..0000000 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgManager.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Deconstruction.Type.Peculiarity; -using UnityEngine; - -namespace XericUI.ReflectionUIGenerator -{ - /// - /// 节点下生成反射属性管理器 - /// - public class XuiavrgManager - { - - /// - /// 装填数据负载 - /// - /// - public void PerformSchemaDrivenHydration(object payload) - { - var type = payload.GetType(); - - var attributes = type.GetCustomAttributes(true); - foreach (var attribute in attributes) - { - if (attribute is not XuiavrgAttribute xuiavrgAttribute) - continue; - - - } - } - } -} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs deleted file mode 100644 index 5ced82a..0000000 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPooledLocator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace XericUI.ReflectionUIGenerator -{ - /// - /// ui·´ÉäÊôÐԶ¨λÆ÷£¬·ÅÔڳضÔÏóµĸù½ڵãÉϣ¬É趨ºýÓÏÂÀ´µÄĿ±꼴¿É - /// - public class XuiavrgPooledLocator : MonoBehaviour - { - - } -} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs new file mode 100644 index 0000000..b4e9228 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 界面属性编组 + /// + [Serializable] + [CreateAssetMenu(fileName = "Xeric UI avr Property group", menuName = "Xeric Library/UI Action Vessel/Reflection UI Gen/UI avr Property Group", order = 10)] + public class XuiavrgPropertyGroupScriptableObject : ScriptableObject, IList + { + public List propertys = new List(); + + public IEnumerator GetEnumerator() + { + return propertys.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)propertys).GetEnumerator(); + } + + public void Add(XuiavrPropertyScriptableObject item) + { + propertys.Add(item); + } + + public void Clear() + { + propertys.Clear(); + } + + public bool Contains(XuiavrPropertyScriptableObject item) + { + return propertys.Contains(item); + } + + public void CopyTo(XuiavrPropertyScriptableObject[] array, int arrayIndex) + { + propertys.CopyTo(array, arrayIndex); + } + + public bool Remove(XuiavrPropertyScriptableObject item) + { + return propertys.Remove(item); + } + + public int Count => propertys.Count; + + public bool IsReadOnly => true; + + public int IndexOf(XuiavrPropertyScriptableObject item) + { + return propertys.IndexOf(item); + } + + public void Insert(int index, XuiavrPropertyScriptableObject item) + { + propertys.Insert(index, item); + } + + public void RemoveAt(int index) + { + propertys.RemoveAt(index); + } + + public XuiavrPropertyScriptableObject this[int index] + { + get => propertys[index]; + set => propertys[index] = value; + } + } +} \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs.meta new file mode 100644 index 0000000..5f0eb3a --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgPropertyGroupScriptableObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bcd1c64483d2457ab9143f086dff511d +timeCreated: 1777024131 \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs index e6d758f..64376f4 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs @@ -1,7 +1,22 @@ -namespace XericUI.ReflectionUIGenerator +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace XericUI.ReflectionUIGenerator { public static class XuiavrgUtilities { - + /// + /// 获取所有运行时成员(字段和属性) + /// + /// + /// + public static IEnumerable GetRuntimeMemberInfo(this Type type) + { + foreach (var field in type.GetRuntimeFields()) + yield return field; + foreach (var property in type.GetRuntimeProperties()) + yield return property; + } } } \ No newline at end of file diff --git a/Runtime/OverrideLayout/XericScrollRect.cs b/Runtime/OverrideLayout/XericScrollRect.cs index 4228461..6dd1a33 100644 --- a/Runtime/OverrideLayout/XericScrollRect.cs +++ b/Runtime/OverrideLayout/XericScrollRect.cs @@ -33,11 +33,11 @@ namespace XericUI.OverrideLayout [Tooltip("容器预加载像素范围")] [SerializeField] protected float m_preloadMargin = 200f; - [Tooltip("容器加载响应可被察觉的变化长度")] [SerializeField] + [Tooltip("容器加载响应可被察觉的变化长度(建议设置为子项的二维最小的尺寸)")] [SerializeField] protected float m_discernibleDistance = 10f; [Tooltip("自动剔除更新速率,当设定趋近零时,实际上是期望每帧执行更新")] [SerializeField] - protected float m_AAMVUpdateRate = .5f; + protected float m_AAMVUpdateRate = .1f; // grid布局组件 [SerializeField] protected Vector2 m_cellSize = new Vector2(100, 100); @@ -261,7 +261,7 @@ namespace XericUI.OverrideLayout // 扩展可见区域边界 var visibleMin = viewportRect.min - new Vector2(m_preloadMargin, m_preloadMargin); - var visibleMax = viewportRect.max + new Vector2(m_preloadMargin, m_preloadMargin); + var visibleMax = viewportRect.max + new Vector2(m_preloadMargin, -m_preloadMargin); // 获取当前滚动归一化位置 // var scrollPos = new Vector2( @@ -397,22 +397,34 @@ namespace XericUI.OverrideLayout private void RefreshContentSizeByChildrenLayouts(List layoutItems) { + if (layoutItems.Count == 0) return; + var first = layoutItems[0]; var last = layoutItems[^1]; var size = first.position - last.position; size = size.Abs(); - var spacing = new Vector2(GridLayoutCalculator.Grid.x * m_spacing.x, GridLayoutCalculator.Grid.y * m_spacing.y); + + // 计算实际使用的行列数 + int maxRow = 0; + int maxColumn = 0; + foreach (var item in layoutItems) + { + maxRow = Mathf.Max(maxRow, item.rowIndex); + maxColumn = Mathf.Max(maxColumn, item.columnIndex); + } + int actualCellCountX = maxColumn + 1; + int actualCellCountY = maxRow + 1; + + var spacing = new Vector2((actualCellCountX - 1) * m_spacing.x, (actualCellCountY - 1) * m_spacing.y); switch (m_startAxis) { case GridLayoutGroup.Axis.Horizontal: - var ySpacing = GridLayoutCalculator.Grid.y * m_spacing.y; size.x = Mathf.Max(size.x, viewport.rect.width); - size.y = Mathf.Max(size.y + spacing.y, m_cellSize.y); + size.y = Mathf.Max(size.y + m_spacing.y, m_cellSize.y); break; case GridLayoutGroup.Axis.Vertical: - var xSpacing = GridLayoutCalculator.Grid.x * m_spacing.x; - size.x = Mathf.Max(size.x + spacing.x, m_cellSize.x); + size.x = Mathf.Max(size.x + m_spacing.x, m_cellSize.x); size.y = Mathf.Max(size.y, viewport.rect.height); break; } @@ -454,7 +466,17 @@ namespace XericUI.OverrideLayout } private List GetChildrenLayout(int childrenCount) - => GridLayoutCalculator.CalculateGridLayout( + { + // 根据约束类型设置合适的 constraintCount + int constraintCount = 0; + if (m_constraint == GridLayoutGroup.Constraint.FixedColumnCount || m_constraint == GridLayoutGroup.Constraint.FixedRowCount) + { + // 这里可以根据实际需求设置合适的约束数量 + // 暂时使用一个默认值,实际项目中可能需要从外部传入或通过其他方式计算 + constraintCount = 3; + } + + return GridLayoutCalculator.CalculateGridLayout( _layoutItems, childCount: childrenCount, containerSize: ContextSize, @@ -464,9 +486,10 @@ namespace XericUI.OverrideLayout startCorner: m_startCorner, startAxis: m_startAxis, constraint: m_constraint, - constraintCount: 0, + constraintCount: constraintCount, childAlignment: m_childAlignment ); + } public void RefreshChildrenLayout() {