属性反射生成界面基本框架。
修复懒加载框的布局问题。
This commit is contained in:
@@ -19,6 +19,17 @@ namespace XericUI.Helper
|
||||
/// <summary>
|
||||
/// 计算所有项目的布局
|
||||
/// </summary>
|
||||
/// <param name="childCount">子项数量</param>
|
||||
/// <param name="containerSize">所需尺寸</param>
|
||||
/// <param name="cellSize">单元尺寸</param>
|
||||
/// <param name="spacing">单元空隙</param>
|
||||
/// <param name="padding">外围空隙</param>
|
||||
/// <param name="startCorner">起始角</param>
|
||||
/// <param name="startAxis">起始轴</param>
|
||||
/// <param name="constraint">容器尺寸</param>
|
||||
/// <param name="constraintCount">需要的列数</param>
|
||||
/// <param name="childAlignment">子项对齐</param>
|
||||
/// <returns></returns>
|
||||
public static List<LayoutItem> CalculateGridLayout(
|
||||
List<LayoutItem> 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(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5661a6f69ad8f9e4a8085712f4ac7e38
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
public interface IXuiavrgAttribute
|
||||
{
|
||||
public void BindProperty(XuiavrgInspectorProperty property);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 998589d7b3414ddd99b69cb128d7e052
|
||||
timeCreated: 1777014404
|
||||
+107
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 属性映射器
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// 属性类型
|
||||
/// </summary>
|
||||
public System.Type ValueType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_valueType != null)
|
||||
return _valueType;
|
||||
return _valueType = Type.GetType(valueType);
|
||||
}
|
||||
}
|
||||
private System.Type _valueType;
|
||||
|
||||
/// <summary>
|
||||
/// 属性类型名称枚举
|
||||
/// </summary>
|
||||
private IEnumerable AssemblyValueTypeNames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_assemblyValueTypeName != null)
|
||||
return _assemblyValueTypeName;
|
||||
_assemblyValueTypeName = new ValueDropdownList<string>();
|
||||
|
||||
// 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<string>(a.Name, a.FullName)));
|
||||
return _assemblyValueTypeName;
|
||||
}
|
||||
}
|
||||
private static ValueDropdownList<string> _assemblyValueTypeName = null;
|
||||
|
||||
/// <summary>
|
||||
/// 直接定义所有需要检查的类型
|
||||
/// </summary>
|
||||
public static IEnumerable<Type> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be0acbdac8e549578dcccf8c076a5433
|
||||
timeCreated: 1776998687
|
||||
@@ -1,7 +1,78 @@
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
using System;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
public class XuiavrgAttribute
|
||||
/// <summary>
|
||||
/// 反射属性标记
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
/// <summary>
|
||||
/// 标记属性
|
||||
/// </summary>
|
||||
/// <param name="labelName">属性显示名称</param>
|
||||
/// <param name="uiPropertyName">属性标记名(对应界面元素名称)</param>
|
||||
/// <param name="unitLabelName">属性显示单位名称</param>
|
||||
/// <param name="mustFillin">是否标记为必填项目</param>
|
||||
/// <param name="inspectorOrder">属性显示顺序</param>
|
||||
/// <param name="groupName">属性组名称(可以是以/&\分割的路径)</param>
|
||||
/// <param name="onTriggerCall">属性附加的动作,调用此内联反射调用的函数</param>
|
||||
/// <param name="size">属性组名称(可以是以/&\分割的路径)</param>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 属性尺寸
|
||||
/// </summary>
|
||||
public enum XuiavrgSize
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Tiny = 1000 - 2,
|
||||
Small = 1000 - 1,
|
||||
Normal = 1000,
|
||||
Big,
|
||||
Large,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 隐藏标记
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class XuiavrgHideAttribute : Attribute, IXuiavrgAttribute
|
||||
{
|
||||
public readonly bool Hide;
|
||||
|
||||
/// <summary>
|
||||
/// 标记属性
|
||||
/// </summary>
|
||||
/// <param name="hideInInspector">隐藏属性</param>
|
||||
public XuiavrgHideAttribute(bool hideInInspector = true)
|
||||
{
|
||||
Hide = hideInInspector;
|
||||
}
|
||||
|
||||
public void BindProperty(XuiavrgInspectorProperty property)
|
||||
{
|
||||
property.hideInInspector = Hide;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12f7aecaa5224304b1058ab9dc04a95f
|
||||
timeCreated: 1776995602
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点下生成反射属性管理器
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 管理器需要挂载在unity节点中,管理器中指定的属性承载节点将用于生成属性内容。
|
||||
///
|
||||
/// 为了准确生成界面属性,首先需要准备界面属性预制体。
|
||||
/// 在包含界面交互元素的预制体跟目录上挂载 <see cref="XuiavrgLocator"/> 脚本,其将作为组件入口,管理所有的交互组件,必须在脚本中添加所有允许交互的元素。
|
||||
/// (注:如果你没有odin插件,那么组件样式可能会与设计的不一致,请务必确保每个数组中只包含一个组件)
|
||||
///
|
||||
/// 然后在工程中创建 <see cref="XuiavrPropertyScriptableObject"/> 对象,指定对应的数据类型,将刚刚创建的界面预制体拖入,这样就完成了两者的绑定。
|
||||
/// 为了方便管理,再创建一个 <see cref="XuiavrgPropertyGroupScriptableObject"/> 对象,它可以成为一个组,快速管理和切换所需的数据集合。
|
||||
///
|
||||
/// 接下来,在管理器中填入使用的 <see cref="XuiavrgPropertyGroupScriptableObject"/> 对象,然后调用 SchemaHydration 方法传入负载,界面顷刻生成。
|
||||
///
|
||||
/// </remarks>
|
||||
public class XuiavrgInspectorManager : MonoBehaviour
|
||||
{
|
||||
public static Func<XuiavrgInspectorProperty> CreatInspectorProperty = () => new XuiavrgInspectorProperty();
|
||||
public static Action<XuiavrgInspectorProperty> GetInspectorProperty = a => {};
|
||||
public static Action<XuiavrgInspectorProperty> ReleaseInspectorProperty = a => a.Clear();
|
||||
public static Action<XuiavrgInspectorProperty> DestoryInspectorProperty = a => {};
|
||||
public static ObjectPool<XuiavrgInspectorProperty> InspectorPropertyPool
|
||||
= new ObjectPool<XuiavrgInspectorProperty>(CreatInspectorProperty, GetInspectorProperty, ReleaseInspectorProperty, DestoryInspectorProperty);
|
||||
|
||||
#if ODIN_INSPECTOR
|
||||
[LabelText("预制体组")]
|
||||
#endif
|
||||
public XuiavrgPropertyGroupScriptableObject propertyUIPrefabsGroup;
|
||||
#if ODIN_INSPECTOR
|
||||
[LabelText("反射属性预览")]
|
||||
#endif
|
||||
public List<XuiavrgInspectorProperty> allProperty = new List<XuiavrgInspectorProperty>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 装填数据负载
|
||||
/// </summary>
|
||||
/// <param name="payload">数据端对象</param>
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 状态数据负载
|
||||
/// </summary>
|
||||
/// <param name="properties">反射属性结合</param>
|
||||
/// <param name="payload">负载实例</param>
|
||||
/// <typeparam name="T">进行检查的负载类型</typeparam>
|
||||
private static void SchemaHydration<T>(List<XuiavrgInspectorProperty> properties, object payload)
|
||||
=> SchemaHydration(properties, typeof(T), payload);
|
||||
|
||||
/// <summary>
|
||||
/// 装填数据负载
|
||||
/// </summary>
|
||||
/// <param name="properties">反射属性集合</param>
|
||||
/// <param name="payloadType">进行检查的负载类型</param>
|
||||
/// <param name="payload">负载实例</param>
|
||||
private static void SchemaHydration(List<XuiavrgInspectorProperty> 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<IXuiavrgAttribute>())
|
||||
{
|
||||
memberAttribute.BindProperty(mw);
|
||||
needSort |= mw.inspectorOrder != 0; // 如果有成员使用了order
|
||||
}
|
||||
properties.Add(mw);
|
||||
}
|
||||
// 排序
|
||||
if (needSort)
|
||||
properties.Sort((property1, property2) => property1.inspectorOrder.CompareTo(property2.inspectorOrder));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 反射属性中间层
|
||||
/// </summary>
|
||||
[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;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 绑定到数据端
|
||||
/// </summary>
|
||||
/// <param name="memberInfo"></param>
|
||||
/// <param name="target"></param>
|
||||
public void RegsiterDataValue(MemberInfo memberInfo, object target)
|
||||
{
|
||||
switch (memberInfo)
|
||||
{
|
||||
case PropertyInfo propertyInfo:
|
||||
RegsiterDataValue(propertyInfo, target);
|
||||
break;
|
||||
case FieldInfo fieldInfo:
|
||||
RegsiterDataValue(fieldInfo, target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定到数据端
|
||||
/// </summary>
|
||||
/// <param name="memberInfo"></param>
|
||||
/// <param name="target"></param>
|
||||
public virtual void RegsiterDataValue(PropertyInfo memberInfo, object target)
|
||||
{
|
||||
valueTarget = target;
|
||||
isProperty = true;
|
||||
propertyInfo = memberInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定到数据端
|
||||
/// </summary>
|
||||
/// <param name="memberInfo"></param>
|
||||
/// <param name="target"></param>
|
||||
public virtual void RegsiterDataValue(FieldInfo memberInfo, object target)
|
||||
{
|
||||
valueTarget = target;
|
||||
isProperty = false;
|
||||
fieldInfo = memberInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定到界面端
|
||||
/// </summary>
|
||||
public virtual void RegsiterUIElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 数据端值发生变更
|
||||
/// </summary>
|
||||
public void CallDataValueChange()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 界面端数据发生变更
|
||||
/// </summary>
|
||||
public void CallUIValueChange()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据端获取值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object GetValueFromTargetMember()
|
||||
{
|
||||
if (isProperty)
|
||||
return propertyInfo.GetValue(valueTarget);
|
||||
else
|
||||
return fieldInfo.GetValue(valueTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置值到数据端
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void SetValueToTargetMember(object value)
|
||||
{
|
||||
if (isProperty)
|
||||
propertyInfo.SetValue(valueTarget, value);
|
||||
else
|
||||
fieldInfo.SetValue(valueTarget, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从界面端获取值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object GetValueFormUIElement()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置值到界面端
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public void SetValueToUIElement(object value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d82ae110adb48b397c09939694cc3b2
|
||||
timeCreated: 1777011022
|
||||
@@ -0,0 +1,305 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// ui反射属性定位器,放在UI对象的根节点上,设定好接下来的目标即可
|
||||
/// </summary>
|
||||
public class XuiavrgLocator : MonoBehaviour
|
||||
{
|
||||
public delegate void AnyTrigger(LocatorProperty locator, object value, int selectStart, int selectEnd);
|
||||
|
||||
#if ODIN_INSPECTOR
|
||||
[LabelText("映射组件")]
|
||||
#endif
|
||||
public List<LocatorProperty> 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;
|
||||
|
||||
/// <summary>
|
||||
/// 支持的组件类型
|
||||
/// </summary>
|
||||
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,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置值
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ui触发事件
|
||||
/// </summary>
|
||||
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("任意触发器无法卸载特定事件"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空ui触发事件
|
||||
/// </summary>
|
||||
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 根据名称组建字典树加速查找(或许不用)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using Deconstruction.Type.Peculiarity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点下生成反射属性管理器
|
||||
/// </summary>
|
||||
public class XuiavrgManager
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 装填数据负载
|
||||
/// </summary>
|
||||
/// <param name="payload"></param>
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// ui反射属性定位器,放在池对象的根节点上,设定好接下来的目标即可
|
||||
/// </summary>
|
||||
public class XuiavrgPooledLocator : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 界面属性编组
|
||||
/// </summary>
|
||||
[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<XuiavrPropertyScriptableObject>
|
||||
{
|
||||
public List<XuiavrPropertyScriptableObject> propertys = new List<XuiavrPropertyScriptableObject>();
|
||||
|
||||
public IEnumerator<XuiavrPropertyScriptableObject> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcd1c64483d2457ab9143f086dff511d
|
||||
timeCreated: 1777024131
|
||||
@@ -1,7 +1,22 @@
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace XericUI.ReflectionUIGenerator
|
||||
{
|
||||
public static class XuiavrgUtilities
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有运行时成员(字段和属性)
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<MemberInfo> GetRuntimeMemberInfo(this Type type)
|
||||
{
|
||||
foreach (var field in type.GetRuntimeFields())
|
||||
yield return field;
|
||||
foreach (var property in type.GetRuntimeProperties())
|
||||
yield return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<GridLayoutCalculator.LayoutItem> 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<GridLayoutCalculator.LayoutItem> 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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user