From b1636d5932e12ce71311189621bd28839cde4221 Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sat, 25 Jul 2026 21:38:19 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=8F=8D=E5=B0=84?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9D=90=E8=B4=A8=E5=8F=8D=E5=B0=84=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Accessors/MaterialPropertyAccessor.cs | 110 +++++++++ .../Accessors/ReflectionValueAccessor.cs | 77 +++++++ .../XuiavrgFieldDropdownValueAttribute.cs | 39 ++++ .../XuiavrgFieldToggleGroupValueAttribute.cs | 39 ++++ .../Attribute/XuiavrgSplitAttribute.cs | 7 +- .../Scripts/AvrgMemberMetaData.cs | 129 ++++------- .../Scripts/AvrgMemberMetaDataSet.cs | 89 ++++---- .../Scripts/AvrgMvvmBinding.cs | 7 +- .../Scripts/Core/IAvrgValueAccessor.cs | 64 ++++++ .../Scripts/XuiFieldTempEntry.cs | 169 +++----------- .../Scripts/XuiavrgFieldTag.cs | 39 +++- .../Scripts/XuiavrgUtilities.cs | 209 ++++++++++++++---- .../AvrgValues/XUIAvrgMDColorValue.cs | 58 +++++ .../AvrgValues/XUIAvrgMDDropdownValue.cs | 100 +++++++++ .../AvrgValues/XUIAvrgMDInputFieldValue.cs | 67 ++++++ .../AvrgValues/XUIAvrgMDLabelValue.cs | 57 +++++ .../AvrgValues/XUIAvrgMDSliderValue.cs | 86 +++++++ .../AvrgValues/XUIAvrgMDTextureValue.cs | 63 ++++++ .../AvrgValues/XUIAvrgMDToggleGroupValue.cs | 114 ++++++++++ .../AvrgValues/XUIAvrgMDToggleValue.cs | 52 +++++ .../AvrgValues/XUIAvrgMDVectorValue.cs | 137 ++++++++++++ Runtime/OverrideUI/XUIAvrgMDValue.cs | 72 +++++- 22 files changed, 1444 insertions(+), 340 deletions(-) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs new file mode 100644 index 0000000..3f9aaf4 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs @@ -0,0 +1,110 @@ +using System; +using UnityEngine; +using UnityEngine.Rendering; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 材质属性访问器 — 通过 Material.Get/Set 系列方法读写 Shader 属性 + /// + public class MaterialPropertyAccessor : IAvrgValueAccessor + { + private readonly Material _material; + private readonly string _propName; + private readonly ShaderPropertyType _shaderType; + + public Type ValueType { get; } + public bool IsReadOnly { get; } + + public MaterialPropertyAccessor( + Material material, + string propName, + ShaderPropertyType shaderType, + Type valueType, + bool isReadOnly = false) + { + _material = material; + _propName = propName; + _shaderType = shaderType; + ValueType = valueType; + IsReadOnly = isReadOnly; + } + + public object GetValue() + { + if (_material == null) return null; + + return _shaderType switch + { + ShaderPropertyType.Float or ShaderPropertyType.Range => _material.GetFloat(_propName), + ShaderPropertyType.Int => _material.GetInt(_propName), + ShaderPropertyType.Color => _material.GetColor(_propName), + ShaderPropertyType.Vector => _material.GetVector(_propName), + ShaderPropertyType.Texture => _material.GetTexture(_propName), + _ => null + }; + } + + public void SetValue(object value) + { + if (IsReadOnly || _material == null) return; + + switch (_shaderType) + { + case ShaderPropertyType.Float: + case ShaderPropertyType.Range: + _material.SetFloat(_propName, Convert.ToSingle(value)); + break; + case ShaderPropertyType.Int: + _material.SetInt(_propName, Convert.ToInt32(value)); + break; + case ShaderPropertyType.Color: + _material.SetColor(_propName, (Color)value); + break; + case ShaderPropertyType.Vector: + _material.SetVector(_propName, (Vector4)value); + break; + case ShaderPropertyType.Texture: + _material.SetTexture(_propName, value as Texture); + break; + } + } + + public string FormatDisplay(object value) + { + if (value == null) return "None"; + if (value is Texture tex && tex != null) return tex.name; + return value.ToString(); + } + } + + /// + /// 材质属性访问器工厂 — 持有 Shader 属性名和类型,创建时绑定 Material 实例 + /// + public class MaterialPropertyAccessorFactory : IAvrgValueAccessorFactory + { + private readonly string _propName; + private readonly ShaderPropertyType _shaderType; + private readonly Type _valueType; + private readonly bool _isReadOnly; + + public MaterialPropertyAccessorFactory( + string propName, + ShaderPropertyType shaderType, + Type valueType, + bool isReadOnly = false) + { + _propName = propName; + _shaderType = shaderType; + _valueType = valueType; + _isReadOnly = isReadOnly; + } + + public IAvrgValueAccessor CreateAccessor(object targetInstance) + { + var material = targetInstance as Material; + if (material == null) return null; + return new MaterialPropertyAccessor(material, _propName, _shaderType, _valueType, _isReadOnly); + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs new file mode 100644 index 0000000..f4bbc76 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs @@ -0,0 +1,77 @@ +using System; +using System.Reflection; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 反射值访问器 — 通过 FieldInfo / PropertyInfo 读写 C# 对象成员 + /// + public class ReflectionValueAccessor : IAvrgValueAccessor + { + private readonly MemberInfo _memberInfo; + private object _targetInstance; + + public Type ValueType { get; } + public bool IsReadOnly { get; } + + public ReflectionValueAccessor(MemberInfo memberInfo, object targetInstance) + { + _memberInfo = memberInfo; + _targetInstance = targetInstance; + + ValueType = memberInfo switch + { + FieldInfo fi => fi.FieldType, + PropertyInfo pi => pi.PropertyType, + _ => typeof(object) + }; + + IsReadOnly = memberInfo is PropertyInfo pi && !pi.CanWrite; + } + + public object GetValue() + { + if (_targetInstance == null) return null; + return _memberInfo switch + { + FieldInfo fi => fi.GetValue(_targetInstance), + PropertyInfo pi => pi.GetValue(_targetInstance), + _ => null + }; + } + + public void SetValue(object value) + { + if (IsReadOnly || _targetInstance == null) return; + switch (_memberInfo) + { + case FieldInfo fi: + fi.SetValue(_targetInstance, value); + break; + case PropertyInfo pi: + pi.SetValue(_targetInstance, value); + break; + } + } + + public string FormatDisplay(object value) => value?.ToString() ?? ""; + } + + /// + /// 反射值访问器工厂 — 持有 MemberInfo,创建时绑定 TargetInstance + /// + public class ReflectionAccessorFactory : IAvrgValueAccessorFactory + { + private readonly MemberInfo _memberInfo; + + public ReflectionAccessorFactory(MemberInfo memberInfo) + { + _memberInfo = memberInfo; + } + + public IAvrgValueAccessor CreateAccessor(object targetInstance) + { + return new ReflectionValueAccessor(_memberInfo, targetInstance); + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs new file mode 100644 index 0000000..1df7fce --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs @@ -0,0 +1,39 @@ +using System; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 为 Dropdown UI 提供预设选项 + /// + /// 使用示例: + /// [XuiavrgFieldDropdownValue("选项A", "选项B", "选项C", MergeMode = PresetOptionsMergeMode.Union)] + /// public string quality = "选项A"; + /// + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public class XuiavrgFieldDropdownValueAttribute : Attribute, IXuiavrgAttribute + { + /// 预设选项数组 + public readonly string[] Options; + + /// 与已有选项(如 enum 名)的合并模式 + public PresetOptionsMergeMode MergeMode { get; set; } = PresetOptionsMergeMode.Replace; + + public XuiavrgFieldDropdownValueAttribute(params string[] options) + { + Options = options ?? Array.Empty(); + } + + public void BindMetaData(AvrgMemberMetaData metaData) + { + if (metaData == null) return; + + metaData.PresetOptions = Options; + metaData.OptionsMergeMode = MergeMode; + + // 自动设置 Tag 为 Dropdown + if (metaData.Tag == XuiavrgFieldTag.None) + metaData.Tag = XuiavrgFieldTag.Dropdown; + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs new file mode 100644 index 0000000..2affa9e --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs @@ -0,0 +1,39 @@ +using System; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 为 ToggleGroup UI 提供预设选项 + /// + /// 使用示例: + /// [XuiavrgFieldToggleGroupValue("低", "中", "高", MergeMode = PresetOptionsMergeMode.Replace)] + /// public string detailLevel = "中"; + /// + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public class XuiavrgFieldToggleGroupValueAttribute : Attribute, IXuiavrgAttribute + { + /// 预设选项数组 + public readonly string[] Options; + + /// 与已有选项(如 enum 名)的合并模式 + public PresetOptionsMergeMode MergeMode { get; set; } = PresetOptionsMergeMode.Replace; + + public XuiavrgFieldToggleGroupValueAttribute(params string[] options) + { + Options = options ?? Array.Empty(); + } + + public void BindMetaData(AvrgMemberMetaData metaData) + { + if (metaData == null) return; + + metaData.PresetOptions = Options; + metaData.OptionsMergeMode = MergeMode; + + // 自动设置 Tag 为 ToggleGroup + if (metaData.Tag == XuiavrgFieldTag.None) + metaData.Tag = XuiavrgFieldTag.ToggleGroup; + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs index 79e7eb6..9e5bcce 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs @@ -8,12 +8,11 @@ namespace XericUI.ReflectionUIGenerator /// 将一个复合类型(如 Vector2)拆分为多个子属性,分别绑定到不同的 UI 元素 /// /// 使用示例: - /// [XuiavrgFieldEntryGroup("speed")] /// [XuiavrgSplit("x", "最小")] /// [XuiavrgSplit("y", "最大")] /// public Vector2 speedRange = new Vector2(0, 100); /// - /// 上述代码创建一个 "speed" 编组的 Entry,内部包含两个子绑定: + /// 上述代码创建一个编组 Entry,内部包含两个子元数据: /// speedRange.x 绑定到 UI 的"最小"输入,speedRange.y 绑定到"最大"输入 /// 子绑定通过 XUIAvrgMDValue.BindingSlots 与预制体上的 UI 组件匹配 /// @@ -39,13 +38,12 @@ namespace XericUI.ReflectionUIGenerator public void BindMetaData(AvrgMemberMetaData metaData) { - // 创建子元数据 + // 创建子元数据 — 纯元数据,不持有实例 var subMetaData = new AvrgMemberMetaData { MemberName = $"{metaData.MemberName}.{PropertyPath}", PropertyPath = PropertyPath, LabelName = DisplayLabel ?? PropertyPath, - TargetInstance = null, // 由运行时闭包解析 Hide = metaData.Hide, Required = metaData.Required, FieldOrder = metaData.FieldOrder, @@ -74,7 +72,6 @@ namespace XericUI.ReflectionUIGenerator if (parentType == null || string.IsNullOrEmpty(propertyPath)) return null; - // 仅支持单层路径 if (propertyPath.Contains('/')) return null; diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs index 1e667fd..6e10eaa 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs @@ -1,57 +1,36 @@ using System; using System.Collections.Generic; -using System.Reflection; namespace XericUI.ReflectionUIGenerator { /// /// 单个成员的元数据数据结构 - /// 记录从反射 + Attribute 标记中提取的所有信息 + /// 纯数据描述 — 不持有反射信息(MemberInfo)和实例引用(TargetInstance) + /// + /// 数据的实际读写通过 IAvrgValueAccessorFactory 在 BuildMDVM 阶段创建的 IAvrgValueAccessor 完成。 + /// 这使得同一个元数据模板可复用于 C# 反射对象、Material Shader 属性等不同数据源。 /// public class AvrgMemberMetaData { - // ============ 反射信息 ============ + // ============ 基本标识 ============ - /// 字段/属性名 + /// 字段/属性名(或 Shader 属性名如 "_MainTex") public string MemberName { get; set; } - /// 反射信息 (FieldInfo 或 PropertyInfo) - public MemberInfo MemberInfo { get; set; } - - /// 值类型 - public Type ValueType { get; set; } - - /// 是否为属性 (false 为字段) - public bool IsProperty { get; set; } - - /// 绑定的目标实例 (可选,Step1 时可空) - public object TargetInstance { get; set; } - - // ============ 拆分属性支持 ============ - - /// - /// 子属性路径(仅拆分属性有效) - /// 用于从父值中访问子字段/属性,如 "x"、"y"、"width" - /// - public string PropertyPath { get; set; } - - /// - /// 拆分子元数据列表 - /// 由 [XuiavrgSplit] 特性填充,每个子元数据对应一个拆分属性 - /// - public List SubMetaDataList { get; set; } - - // ============ 从 IXuiavrgAttribute 融合的元数据 ============ - - /// 显示标签 + /// UI 显示标签 public string LabelName { get; set; } - /// 单位文本 + /// 单位文本(如 "m/s") public string UnitLabelName { get; set; } /// 格式化字符串(如 "{0:F2}") public string FormatText { get; set; } + /// 值类型 + public Type ValueType { get; set; } + + // ============ UI 映射 ============ + /// 功能标记 public XuiavrgFieldTag Tag { get; set; } = XuiavrgFieldTag.None; @@ -67,21 +46,12 @@ namespace XericUI.ReflectionUIGenerator /// 是否在 UI 中隐藏 public bool Hide { get; set; } - /// 值变更时触发的反射调用方法名 - public string OnTriggerCall { get; set; } + // ============ 默认值与验证 ============ - /// - /// 类的初始默认值(由类字段初始化器决定) - /// 例如 public float speed = 10f; → DefaultValue = 10f - /// + /// 默认值(用于 UI 初始化和回退参考) public object DefaultValue { get; set; } - // ============ 属性检查 (由 [PropertyCheck] 填充) ============ - - /// - /// 判断依据标识 - /// 默认使用 ValueType.FullName 与 AvrgValueRangeConfig 中的条目匹配 - /// + /// 属性检查依据标识(默认使用 ValueType.FullName) public string PropertyCheckKey { get; set; } /// 是否必填 @@ -99,47 +69,46 @@ namespace XericUI.ReflectionUIGenerator /// 是否启用无效值状态检查 public bool ValidateInvalid { get; set; } - // ============ 原始 Attribute 引用 ============ + /// Shader Range 属性下限(null 表示非 Range 属性) + public float? RangeMin { get; set; } + + /// Shader Range 属性上限(null 表示非 Range 属性) + public float? RangeMax { get; set; } + + // ============ 拆分属性支持 ============ /// - /// 收集到的所有 IXuiavrgAttribute 列表 - /// 保留原始引用,供扩展和自定义处理使用 + /// 子属性路径(仅拆分属性有效) + /// 用于从父值中访问子字段/属性,如 "x"、"y"、"width" /// + public string PropertyPath { get; set; } + + /// + /// 拆分子元数据列表 + /// 由 [XuiavrgSplit] 特性填充 + /// + public List SubMetaDataList { get; set; } + + // ============ 预设选项 (Dropdown/ToggleGroup) ============ + + /// 预设选项列表 + public IReadOnlyList PresetOptions { get; set; } + + /// 预设选项合并模式 + public PresetOptionsMergeMode OptionsMergeMode { get; set; } + + // ============ 扩展 ============ + + /// 收集到的所有 IXuiavrgAttribute 列表 public List Attributes { get; set; } = new List(); - /// - /// 从绑定目标获取值 - /// - public object GetValue() - { - if (TargetInstance == null) return null; - switch (MemberInfo) - { - case PropertyInfo pi: - return pi.GetValue(TargetInstance); - case FieldInfo fi: - return fi.GetValue(TargetInstance); - default: - return null; - } - } + // ============ ★ 核心变更 — 值访问器工厂 ============ /// - /// 设置值到绑定目标 + /// 值访问器工厂 + /// 在元数据提取阶段设置(持有 MemberInfo 或 Shader 属性信息) + /// 在 BuildMDVM 阶段调用 CreateAccessor(targetInstance) 创建真正的读写通道 /// - public void SetValue(object value) - { - if (TargetInstance == null) return; - switch (MemberInfo) - { - case PropertyInfo pi: - if (pi.CanWrite) - pi.SetValue(TargetInstance, value); - break; - case FieldInfo fi: - fi.SetValue(TargetInstance, value); - break; - } - } + public IAvrgValueAccessorFactory AccessorFactory { get; set; } } } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs index 8e174ae..56be430 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs @@ -8,7 +8,7 @@ namespace XericUI.ReflectionUIGenerator { /// /// 元数据集 - /// 包含从对象反射提取的完整元数据,以及用于生成 MVVM 绑定的 BuildMDVM() 方法 + /// 包含从对象/材质反射提取的完整元数据,以及用于生成 MVVM 绑定的 BuildMDVM() 方法 /// public class AvrgMemberMetaDataSet { @@ -33,7 +33,6 @@ namespace XericUI.ReflectionUIGenerator /// internal void MergeGroupEntries() { - // 提取编组和非编组 var grouped = _metaDataList .Where(m => !string.IsNullOrEmpty(m.GroupPath)) .GroupBy(m => m.GroupPath) @@ -47,9 +46,7 @@ namespace XericUI.ReflectionUIGenerator foreach (var gEntry in grouped) { - // 编组内第一个成员作为主元数据 var first = gEntry.First(); - // 收集组内所有成员的 SubMetaDataList(合并拆分属性) foreach (var member in gEntry) { if (member.SubMetaDataList?.Count > 0) @@ -62,9 +59,7 @@ namespace XericUI.ReflectionUIGenerator merged.Add(first); } - // 非编组直接追加 merged.AddRange(nonGrouped); - _metaDataList = merged; } @@ -82,9 +77,10 @@ namespace XericUI.ReflectionUIGenerator } /// - /// 为元数据生成对应的 MVVM 值绑定模板 + /// 为元数据生成对应的 MVVM 值绑定 + /// 通过 AccessorFactory 创建 IAvrgValueAccessor 完成数据读写 /// - /// 可选的业务目标对象(如果元数据中没有绑定目标,则在此传入) + /// 目标实例(C# 对象、Material 等) /// 可选的冲突解决配置 /// 绑定集合 public AvrgMvvmBindingCollection BuildMDVM( @@ -92,30 +88,46 @@ namespace XericUI.ReflectionUIGenerator AvrgMvvmResolverConfig resolverConfig = null) { var bindingCollection = new AvrgMvvmBindingCollection(); + bindingCollection.SourceMetaDataSet = this; var scheduler = new AvrgMvvmScheduler(resolverConfig); foreach (var metaData in _metaDataList) { - // 确定目标实例 - var target = metaData.TargetInstance ?? businessTarget; - if (target == null) + if (metaData.AccessorFactory == null) continue; + if (businessTarget == null) + { + Debug.LogWarning( + $"[Xuiavrg] 元数据 '{metaData.MemberName}' 缺少 targetInstance,跳过绑定"); continue; + } + + var accessor = metaData.AccessorFactory.CreateAccessor(businessTarget); + if (accessor == null) continue; // 创建主绑定 var binding = new AvrgMvvmBinding { MetaData = metaData, - ReadFunc = () => metaData.GetValue(), + ReadFunc = () => accessor.GetValue(), + Accessor = accessor, }; var capturedBinding = binding; - binding.WriteAction = (val) => - { - metaData.SetValue(val); - scheduler.MarkDataDirty(capturedBinding); - }; - // 如果元数据有拆分子属性,创建子绑定 + if (accessor.IsReadOnly) + { + binding.WriteAction = null; + } + else + { + binding.WriteAction = (val) => + { + accessor.SetValue(val); + scheduler.MarkDataDirty(capturedBinding); + }; + } + + // 拆分属性子绑定 if (metaData.SubMetaDataList?.Count > 0) { binding.SubBindings = new List(); @@ -128,14 +140,12 @@ namespace XericUI.ReflectionUIGenerator var subBinding = new AvrgMvvmBinding { MetaData = subMetaData, - // 从父值中读取子属性 - ReadFunc = () => ReadSubProperty(metaData.GetValue(), subPath), - // 修改子属性并写回父值 + ReadFunc = () => ReadSubProperty(accessor.GetValue(), subPath), WriteAction = (val) => { - var parentVal = metaData.GetValue(); + var parentVal = accessor.GetValue(); var modifiedParent = WriteSubProperty(parentVal, subPath, val); - metaData.SetValue(modifiedParent); + accessor.SetValue(modifiedParent); scheduler.MarkDataDirty(capturedBinding); }, }; @@ -155,16 +165,11 @@ namespace XericUI.ReflectionUIGenerator // ============ 子属性访问辅助方法 ============ - /// - /// 从父值中读取子属性(支持单层路径如 "x"、"y") - /// 例如:ReadSubProperty(Vector2(1,2), "x") → 1f - /// private static object ReadSubProperty(object parentValue, string propertyPath) { if (parentValue == null || string.IsNullOrEmpty(propertyPath)) return null; - // 仅支持单层路径 if (propertyPath.Contains('/')) { Debug.LogWarning($"[Xuiavrg] 暂不支持多层嵌套路径 '{propertyPath}'"); @@ -182,10 +187,6 @@ namespace XericUI.ReflectionUIGenerator return null; } - /// - /// 修改父值中的子属性并返回修改后的父值 - /// 适用于值类型(struct)和引用类型 - /// private static object WriteSubProperty(object parentValue, string propertyPath, object value) { if (parentValue == null || string.IsNullOrEmpty(propertyPath)) @@ -221,8 +222,6 @@ namespace XericUI.ReflectionUIGenerator /// /// 对当前元数据集执行属性检查验证 /// - /// 值范围配置(可选,不传入则不执行范围检查) - /// 验证结果集合 public AvrgValidationResultSet Validate(AvrgValueRangeConfig valueRangeConfig = null) { var resultSet = new AvrgValidationResultSet(); @@ -232,7 +231,6 @@ namespace XericUI.ReflectionUIGenerator if (metaData.Hide) continue; - // 只有标记了 PropertyCheck 的才验证 if (!metaData.Required && !metaData.ValidateReasonable && !metaData.ValidateWarning && !metaData.ValidateInvalid) { @@ -240,13 +238,13 @@ namespace XericUI.ReflectionUIGenerator { MetaData = metaData, Result = AvrgMemberCheckResult.Skipped, - CurrentValueString = metaData.GetValue()?.ToString(), + CurrentValueString = metaData.DefaultValue?.ToString(), Message = "未标记属性检查", }); continue; } - var value = metaData.GetValue(); + var value = metaData.DefaultValue; var checkResult = CheckSingleMember(metaData, value, valueRangeConfig); resultSet.Results.Add(checkResult); } @@ -254,9 +252,6 @@ namespace XericUI.ReflectionUIGenerator return resultSet; } - /// - /// 验证单个成员 - /// private AvrgMemberValidationResult CheckSingleMember( AvrgMemberMetaData metaData, object value, @@ -268,7 +263,6 @@ namespace XericUI.ReflectionUIGenerator CurrentValueString = value?.ToString() ?? "(null)", }; - // 根据 CheckKey 查找值范围规则 AvrgValueRangeEntry rangeEntry = null; string checkKey = !string.IsNullOrEmpty(metaData.PropertyCheckKey) ? metaData.PropertyCheckKey @@ -279,22 +273,19 @@ namespace XericUI.ReflectionUIGenerator rangeEntry = rangeConfig.FindEntry(checkKey); } - // ——— 优先级 1: 空值检查 (Required) ——— + // 优先级 1: 空值检查 (Required) if (metaData.Required) { bool isEmpty = false; - // 引用类型 null if (value == null) { isEmpty = true; } - // 字符串 null/空 else if (value is string str) { isEmpty = string.IsNullOrEmpty(str); } - // 使用 ValueRangeEntry 的 IsEmpty 判断 else if (rangeEntry != null && !rangeEntry.skipEmptyCheck) { isEmpty = rangeEntry.IsEmpty(value); @@ -308,7 +299,6 @@ namespace XericUI.ReflectionUIGenerator } } - // 如果没有范围配置,无法继续检查 if (rangeEntry == null) { result.Result = AvrgMemberCheckResult.Valid; @@ -316,7 +306,7 @@ namespace XericUI.ReflectionUIGenerator return result; } - // ——— 优先级 2: 合理值检查(最高优先级,命中即通过) ——— + // 优先级 2: 合理值检查 if (metaData.ValidateReasonable && rangeEntry.IsReasonable(value)) { result.Result = AvrgMemberCheckResult.Valid; @@ -324,7 +314,7 @@ namespace XericUI.ReflectionUIGenerator return result; } - // ——— 优先级 3: 无效值检查 ——— + // 优先级 3: 无效值检查 if (metaData.ValidateInvalid && rangeEntry.IsInvalid(value)) { result.Result = AvrgMemberCheckResult.Invalid; @@ -332,7 +322,7 @@ namespace XericUI.ReflectionUIGenerator return result; } - // ——— 优先级 4: 警告值检查 ——— + // 优先级 4: 警告值检查 if (metaData.ValidateWarning && rangeEntry.IsWarning(value)) { result.Result = AvrgMemberCheckResult.Warning; @@ -340,7 +330,6 @@ namespace XericUI.ReflectionUIGenerator return result; } - // ——— 最终: 通过 ——— result.Result = AvrgMemberCheckResult.Valid; result.Message = "通过"; return result; diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs index 970b894..c8faa0f 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; namespace XericUI.ReflectionUIGenerator { @@ -20,6 +19,12 @@ namespace XericUI.ReflectionUIGenerator /// 写入值到数据源的委托 public Action WriteAction { get; set; } + /// + /// 值访问器引用(在 BuildMDVM 时创建,供 UI 组件使用) + /// 提供 ValueType / IsReadOnly / FormatDisplay 等扩展能力 + /// + public IAvrgValueAccessor Accessor { get; set; } + /// /// 拆分属性子绑定列表 /// 由 [XuiavrgSplit] 特性展开生成,每个子绑定对应一个拆分后的子属性 diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs new file mode 100644 index 0000000..77841e2 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs @@ -0,0 +1,64 @@ +using System; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 预设选项合并模式 + /// 用于 Dropdown/ToggleGroup 的预设选项与已有选项(如 enum 名)的合并策略 + /// + public enum PresetOptionsMergeMode + { + /// 完全覆盖现有选项 + Replace, + + /// 保留两者交集 + Intersection, + + /// 保留两者并集(去重合并) + Union, + + /// 从现有选项中移除预设选项(差集) + Difference, + } + + /// + /// 值访问器 — 统一的数据读写通道 + /// 解耦"数据的物理存储方式"和"业务层的读写需求" + /// + /// C# 反射对象和 Material Shader 属性通过各自实现此接口, + /// 向 MVVM 绑定层暴露完全相同的数据交互约定。 + /// + public interface IAvrgValueAccessor + { + /// 值的 C# 类型 + Type ValueType { get; } + + /// 是否只读(如 Shader Keywords 不可运行时修改) + bool IsReadOnly { get; } + + /// 从数据源读取当前值 + object GetValue(); + + /// 将值写入数据源(IsReadOnly 时忽略) + void SetValue(object value); + + /// 将值格式化为 UI 显示字符串 + string FormatDisplay(object value); + } + + /// + /// 值访问器工厂 — 在"元数据模板"阶段创建,延迟到"绑定实例"时生产 Accessor + /// + /// 设计意图: + /// 1. 元数据提取阶段:只描述"如何访问数据",不绑定具体实例 + /// 2. BuildMDVM 阶段:传入实例,通过 Factory 生产 Accessor + /// 3. 同一套元数据模板可复用于不同实例 + /// + public interface IAvrgValueAccessorFactory + { + /// 为给定目标实例创建值访问器 + /// 目标实例(C# 对象、Material 等) + /// 值访问器实例,targetInstance 类型不匹配时返回 null + IAvrgValueAccessor CreateAccessor(object targetInstance); + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs index fdaaae4..e658f02 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs @@ -74,24 +74,33 @@ namespace XericUI.ReflectionUIGenerator /// /// 字段临时 Entry 实现类 /// 作为数据对象和 UI 组件之间的中间缓存层 + /// + /// UI 交互事件的分配通过 XUIAvrgMDValue.BindToAccessor() 的多态机制完成, + /// 各类 XUIAvrgMDValue 子类自行管理其专属 UI 组件的交互订阅。 /// public class XuiFieldTempEntry : IFieldEntry { #region 对象池 - internal static ObjectPool Pool { get; private set; } - - static XuiFieldTempEntry() + private static ObjectPool _pool; + internal static ObjectPool Pool { - Pool = new ObjectPool( - createFunc: () => new XuiFieldTempEntry(), - actionOnGet: entry => entry.OnEntryCreate(), - actionOnRelease: entry => entry.OnEntryRelease(), - actionOnDestroy: entry => entry.OnEntryDestroy(), - collectionCheck: false, - defaultCapacity: 32, - maxSize: 512 - ); + get + { + if (_pool == null) + { + _pool = new ObjectPool( + createFunc: () => new XuiFieldTempEntry(), + actionOnGet: entry => entry.OnEntryCreate(), + actionOnRelease: entry => entry.OnEntryRelease(), + actionOnDestroy: entry => entry.OnEntryDestroy(), + collectionCheck: false, + defaultCapacity: 32, + maxSize: 512 + ); + } + return _pool; + } } /// 从池中获取 Entry 实例 @@ -113,7 +122,7 @@ namespace XericUI.ReflectionUIGenerator private GameObject _uiInstance; private GameObject _boundPrefab; private int _fieldOrder; - private bool _uiBound; // 防止重复绑定 + private bool _uiBound; #endregion @@ -166,9 +175,6 @@ namespace XericUI.ReflectionUIGenerator #region 初始化方法 - /// - /// 设置 Entry 基本属性 - /// public void Initialize(string entryName, XuiavrgFieldTag tag = XuiavrgFieldTag.None, int tagIndex = 0) { _entryName = entryName; @@ -179,18 +185,12 @@ namespace XericUI.ReflectionUIGenerator _uiBound = false; } - /// - /// 设置父 Entry(用于嵌套) - /// public void SetParent(IFieldEntry parent) { _parent = parent; _entryPath = null; } - /// - /// 添加子 Entry - /// public void AddChild(XuiFieldTempEntry child) { if (child == null) return; @@ -198,9 +198,6 @@ namespace XericUI.ReflectionUIGenerator _children.Add(child); } - /// - /// 添加 MVVM 绑定到此 Entry - /// public void AddBinding(AvrgMvvmBinding binding, AvrgMvvmScheduler scheduler) { if (binding == null) return; @@ -253,7 +250,7 @@ namespace XericUI.ReflectionUIGenerator /// /// 将 XUIAvrgMDValue 与 MVVM 绑定连接 - /// 支持主绑定 + 拆分属性子绑定(通过 BindingSlots) + /// 通过多态 BindToAccessor 让 UI 子类自行管理交互事件和显示更新 /// private void BindComponentWithBinding(XUIAvrgMDValue mdValue) { @@ -261,39 +258,27 @@ namespace XericUI.ReflectionUIGenerator var primaryBinding = _bindings[0]; - // ——— 1. 主绑定:数据 → UI ——— - primaryBinding.OnUIDataChanged += value => UpdateUIComponent(mdValue, value); + // ★ 多态调用: UI 子类自行处理交互事件和显示更新 + mdValue.BindToAccessor(primaryBinding.Accessor, primaryBinding, _scheduler); - // ——— 主绑定:UI → 数据 ——— - WireUpUIEvents(mdValue, primaryBinding); - - // ——— 2. 拆分属性子绑定匹配 ——— + // 拆分属性子绑定处理 if (primaryBinding.SubBindings?.Count > 0 && mdValue.AvailableBindingSlots?.Count > 0) { - // 收集所有子节点上的 XUIAvrgMDValue var childMDValues = new List(); mdValue.GetComponentsInChildren(true, childMDValues); foreach (var slot in mdValue.AvailableBindingSlots) { - // 按 propertyName 匹配子绑定 var subBinding = primaryBinding.SubBindings.Find( sb => sb.MetaData?.PropertyPath == slot.propertyName); if (subBinding == null) continue; - // 在子节点中查找匹配 Tag 的 XUIAvrgMDValue foreach (var childMD in childMDValues) { - if (childMD == mdValue) continue; // 跳过自身 + if (childMD == mdValue) continue; if (childMD.EntryTag == slot.tag || slot.tag == XuiavrgFieldTag.None) { - // 子绑定:数据 → UI - subBinding.OnUIDataChanged += value => UpdateUIComponent(childMD, value); - - // 子绑定:UI → 数据(订阅子节点的交互事件) - WireUpUIEvents(childMD, subBinding); - - // 广播子元数据到子节点样式组件 + childMD.BindToAccessor(subBinding.Accessor, subBinding, _scheduler); childMD.InitializeWithMetaData(subBinding.MetaData); break; } @@ -301,104 +286,10 @@ namespace XericUI.ReflectionUIGenerator } } - // 广播主元数据到 XUIAvrgMDValue(触发 IXUIAvrgMDValueStyle 子组件) + // 广播主元数据到样式组件 mdValue.InitializeWithMetaData(primaryBinding.MetaData); } - /// - /// 订阅 UI 组件的交互事件 → 写入绑定 - /// - private void WireUpUIEvents(XUIAvrgMDValue mdValue, AvrgMvvmBinding binding) - { - if (mdValue.TryGetComponent(out UnityEngine.UI.Button uiButton)) - { - uiButton.onClick.AddListener(() => - { - binding.WriteFromUI(true); - _scheduler?.MarkUIDirty(binding); - }); - } - - if (mdValue.TryGetComponent(out UnityEngine.UI.Toggle uiToggle)) - { - uiToggle.onValueChanged.AddListener(val => - { - binding.WriteFromUI(val); - _scheduler?.MarkUIDirty(binding); - }); - } - - if (mdValue.TryGetComponent(out UnityEngine.UI.Slider uiSlider)) - { - uiSlider.onValueChanged.AddListener(val => - { - binding.WriteFromUI(val); - _scheduler?.MarkUIDirty(binding); - }); - } - - if (mdValue.TryGetComponent(out UnityEngine.UI.InputField uiInput)) - { - uiInput.onEndEdit.AddListener(val => - { - binding.WriteFromUI(val); - _scheduler?.MarkUIDirty(binding); - }); - } - - if (mdValue.TryGetComponent(out UnityEngine.UI.Dropdown uiDropdown)) - { - uiDropdown.onValueChanged.AddListener(val => - { - binding.WriteFromUI(val); - _scheduler?.MarkUIDirty(binding); - }); - } - } - - /// - /// 更新 UI 组件的显示值(通过 XUIAvrgMDValue 所在 GameObject 上的具体组件) - /// - private static void UpdateUIComponent(XUIAvrgMDValue mdValue, object value) - { - if (mdValue == null || value == null) return; - - // Text - if (mdValue.TryGetComponent(out UnityEngine.UI.Text uiText)) - { - uiText.text = value.ToString(); - return; - } - - // Toggle - if (value is bool boolVal && mdValue.TryGetComponent(out UnityEngine.UI.Toggle uiToggle)) - { - uiToggle.isOn = boolVal; - return; - } - - // Slider - if (value is float floatVal && mdValue.TryGetComponent(out UnityEngine.UI.Slider uiSlider)) - { - uiSlider.value = floatVal; - return; - } - - // InputField - if (mdValue.TryGetComponent(out UnityEngine.UI.InputField uiInput)) - { - uiInput.text = value.ToString(); - return; - } - - // Dropdown - if (value is int intVal && mdValue.TryGetComponent(out UnityEngine.UI.Dropdown uiDropdown)) - { - uiDropdown.value = intVal; - return; - } - } - #endregion #region 生命周期 @@ -419,7 +310,6 @@ namespace XericUI.ReflectionUIGenerator var value = binding.ReadFromDataSource(); binding.NotifyUIUpdate(value); - // 子绑定也做初始值推送 if (binding.SubBindings?.Count > 0) { foreach (var subBinding in binding.SubBindings) @@ -446,7 +336,6 @@ namespace XericUI.ReflectionUIGenerator foreach (var binding in _bindings) { binding.ClearUIEvent(); - // 子绑定也清理 if (binding.SubBindings?.Count > 0) { foreach (var sub in binding.SubBindings) diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgFieldTag.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgFieldTag.cs index 54281d8..c0f5220 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgFieldTag.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgFieldTag.cs @@ -11,41 +11,56 @@ namespace XericUI.ReflectionUIGenerator public enum XuiavrgFieldTag { None = 0, - + /// 标题文本 / Label Label = 1 << 0, - + /// 内容值 / 数值上下文 Context = 1 << 1, - + /// 单位文本 Unit = 1 << 2, - + /// 布尔开关 / Toggle Toggle = 1 << 3, - + /// 按钮触发器 Button = 1 << 4, - + /// 滑块 / 进度条 Slider = 1 << 5, - + /// 下拉选择 Dropdown = 1 << 6, - + /// 输入值 / InputField Input = 1 << 7, - + /// 单选项 / Radio Select = 1 << 8, - + /// 符号 / 图标 Sign = 1 << 9, - + /// 编辑区域 / 多行文本 Edit = 1 << 10, - + /// 输出区域 / 只读展示 Output = 1 << 11, + + /// 颜色 / Color + Color = 1 << 12, + + /// 纹理预览 / Texture + Texture = 1 << 13, + + /// 向量值组 / Vector (Vector2/3/4) + Vector = 1 << 14, + + /// 开关组 / ToggleGroup + ToggleGroup = 1 << 15, + + /// 只读展示 / ReadOnly + ReadOnly = 1 << 16, } } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs index 79bd6aa..5c6b17e 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using UnityEngine; +using UnityEngine.Rendering; namespace XericUI.ReflectionUIGenerator { @@ -18,19 +20,20 @@ namespace XericUI.ReflectionUIGenerator yield return property; } + // ============ C# 对象: 元数据提取(纯元数据,不绑定实例)============ + /// - /// 从任意对象提取 AvrgMemberMetaData 元数据集 - /// 反射识别成员、识别 IXuiavrgAttribute 标记、执行标记绑定的完整流程 + /// 从类型提取 AvrgMemberMetaData 元数据集(不含实例绑定) + /// 用于构建元数据模板,可复用于不同实例 /// - /// 待提取的数据对象 - /// 提取完成的元数据集 - public static AvrgMemberMetaDataSet ExtractAvrgMemberMetaData(this object source) + /// 待提取的类型 + /// 纯元数据集(无实例引用,由 AccessorFactory 持有反射信息) + public static AvrgMemberMetaDataSet ExtractAvrgMemberMetaData(this Type type) { - if (source == null) - throw new ArgumentNullException(nameof(source)); + if (type == null) + throw new ArgumentNullException(nameof(type)); var metaDataSet = new AvrgMemberMetaDataSet(); - var type = source.GetType(); foreach (var memberInfo in type.GetRuntimeMemberInfo()) { @@ -39,52 +42,30 @@ namespace XericUI.ReflectionUIGenerator if (hideAttr != null && hideAttr.Hide) continue; - // 创建元数据 + // 识别值类型 + Type valueType = memberInfo switch + { + PropertyInfo pi => pi.PropertyType, + FieldInfo fi => fi.FieldType, + _ => typeof(object) + }; + + // 创建元数据 — 不持有 MemberInfo 和 TargetInstance var metaData = new AvrgMemberMetaData { MemberName = memberInfo.Name, - MemberInfo = memberInfo, - TargetInstance = source, + ValueType = valueType, + AccessorFactory = new ReflectionAccessorFactory(memberInfo), + PropertyCheckKey = valueType?.FullName, }; - // 识别值类型和 IsProperty - switch (memberInfo) - { - case PropertyInfo pi: - metaData.IsProperty = true; - metaData.ValueType = pi.PropertyType; - break; - case FieldInfo fi: - metaData.IsProperty = false; - metaData.ValueType = fi.FieldType; - break; - } - - // 提取字段默认值(类初始值) - // Unity 序列化字段可以通过反射读取实例上的当前值作为"默认"参考 - // 但更准确的在类声明阶段的初始值需要通过运行时观察或 Fallback 判断 - // 此处策略:如果 TargetInstance 非 null,直接 ReadValue 作为 DefaultValue - if (source != null && memberInfo is FieldInfo fi2) - { - metaData.DefaultValue = fi2.GetValue(source); - } - else if (source != null && memberInfo is PropertyInfo pi2 && pi2.CanRead) - { - metaData.DefaultValue = pi2.GetValue(source); - } - - // 设置 PropertyCheckKey 默认值(用类型全称) - if (metaData.ValueType != null) - metaData.PropertyCheckKey = metaData.ValueType.FullName; - - // 收集所有 IXuiavrgAttribute + // 收集并执行所有 IXuiavrgAttribute var attributes = memberInfo.GetCustomAttributes() .OfType() .ToList(); metaData.Attributes = attributes; - // 按序执行 BindMetaData foreach (var attr in attributes) { attr.BindMetaData(metaData); @@ -103,11 +84,147 @@ namespace XericUI.ReflectionUIGenerator } /// - /// 从任意对象提取 AvrgMemberMetaData 元数据集的泛型版本 + /// 从对象实例提取元数据并一步生成绑定(便捷方法) + /// 内部调用 Type.ExtractAvrgMemberMetaData() + BuildMDVM() /// - public static AvrgMemberMetaDataSet ExtractAvrgMemberMetaData(this T source) + public static AvrgMvvmBindingCollection BuildBindingsFromObject( + this object source, + AvrgMvvmResolverConfig resolverConfig = null) { - return ExtractAvrgMemberMetaData((object)source); + if (source == null) + throw new ArgumentNullException(nameof(source)); + + var metaDataSet = source.GetType().ExtractAvrgMemberMetaData(); + return metaDataSet.BuildMDVM(source, resolverConfig); + } + + // ============ Material: 元数据提取 ============ + + /// + /// 从 Material 的 Shader 属性中提取元数据集 + /// + /// 目标 Material + /// 纯元数据集(AccessorFactory 持有 MaterialPropertyAccessorFactory) + public static AvrgMemberMetaDataSet ExtractFromMaterial(this Material material) + { + if (material == null) + throw new ArgumentNullException(nameof(material)); + + var shader = material.shader; + if (shader == null) + { + Debug.LogError("[Xuiavrg] Material 没有关联的 Shader"); + return null; + } + + var metaDataSet = new AvrgMemberMetaDataSet(); + int propCount = shader.GetPropertyCount(); + + for (int i = 0; i < propCount; i++) + { + var propName = shader.GetPropertyName(i); + var shaderType = shader.GetPropertyType(i); + var desc = shader.GetPropertyDescription(i); + var flags = shader.GetPropertyFlags(i); + + // 跳过隐藏属性 + if ((flags & UnityEngine.Rendering.ShaderPropertyFlags.HideInInspector) != 0) + continue; + + // ShaderPropertyType → C# Type + Tag + (Type valueType, XuiavrgFieldTag tag) = MapShaderPropertyType(shaderType, propName); + + var metaData = new AvrgMemberMetaData + { + MemberName = propName, + ValueType = valueType, + LabelName = !string.IsNullOrEmpty(desc) ? desc : FormatPropertyName(propName), + Tag = tag, + AccessorFactory = new MaterialPropertyAccessorFactory(propName, shaderType, valueType), + }; + + // 读取当前值作为 DefaultValue + metaData.DefaultValue = ReadMaterialProperty(material, propName, shaderType); + + // Range 属性:提取范围限制 + if (shaderType == ShaderPropertyType.Range) + { + var limits = shader.GetPropertyRangeLimits(i); + metaData.RangeMin = limits.x; + metaData.RangeMax = limits.y; + } + + metaDataSet.Add(metaData); + } + + // ===== Shader Keywords 提取(只读)===== + try + { + var keywordSpace = shader.keywordSpace; + foreach (var kw in keywordSpace.keywords) + { + bool isEnabled = material.IsKeywordEnabled(kw.name); + var kwMetaData = new AvrgMemberMetaData + { + MemberName = $"kwd_{kw.name}", + ValueType = typeof(bool), + LabelName = $"[Keyword] {kw.name}", + Tag = XuiavrgFieldTag.Toggle, + DefaultValue = isEnabled, + AccessorFactory = new MaterialPropertyAccessorFactory( + kw.name, + ShaderPropertyType.Float, // Keywords 不是标准属性,用 Float 占位 + typeof(bool), + isReadOnly: true), + }; + metaDataSet.Add(kwMetaData); + } + } + catch (Exception e) + { + Debug.LogWarning($"[Xuiavrg] 提取 Shader Keywords 失败: {e.Message}"); + } + + metaDataSet.SortByFieldOrder(); + return metaDataSet; + } + + private static (Type valueType, XuiavrgFieldTag tag) MapShaderPropertyType( + ShaderPropertyType shaderType, string propName) + { + return shaderType switch + { + ShaderPropertyType.Float => (typeof(float), XuiavrgFieldTag.Input), + ShaderPropertyType.Range => (typeof(float), XuiavrgFieldTag.Slider), + ShaderPropertyType.Int => (typeof(int), XuiavrgFieldTag.Input), + ShaderPropertyType.Color => (typeof(Color), XuiavrgFieldTag.Color), + ShaderPropertyType.Vector => (typeof(Vector4), XuiavrgFieldTag.Vector), + ShaderPropertyType.Texture => (typeof(Texture), XuiavrgFieldTag.Texture), + _ => (typeof(object), XuiavrgFieldTag.None), + }; + } + + private static object ReadMaterialProperty(Material material, string propName, + ShaderPropertyType shaderType) + { + return shaderType switch + { + ShaderPropertyType.Float or ShaderPropertyType.Range => material.GetFloat(propName), + ShaderPropertyType.Int => material.GetInt(propName), + ShaderPropertyType.Color => material.GetColor(propName), + ShaderPropertyType.Vector => material.GetVector(propName), + ShaderPropertyType.Texture => material.GetTexture(propName), + _ => null + }; + } + + /// + /// 属性名格式化(_MainTex → MainTex, _ShadowColor → Shadow Color) + /// + private static string FormatPropertyName(string propName) + { + if (string.IsNullOrEmpty(propName)) return propName; + return propName.TrimStart('_').Replace('_', ' '); } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs new file mode 100644 index 0000000..9790e8a --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs @@ -0,0 +1,58 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 颜色值组件 — 处理 Color 类型的显示 + /// 通过 Image.color 展示当前颜色值 + /// 颜色修改可通过 RGB 子节点 InputField 或额外的颜色选择逻辑完成 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDColorValue")] + public class XUIAvrgMDColorValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("用于展示颜色的 Image 组件(未设置则自动查找)")] + private Image _colorDisplay; + + private void Awake() + { + if (_colorDisplay == null) _colorDisplay = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null) return; + + // 数据 → UI + binding.OnUIDataChanged += value => + { + if (value is Color color && _colorDisplay != null) + { + _colorDisplay.color = color; + } + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + if (initialValue is Color initColor && _colorDisplay != null) + { + _colorDisplay.color = initColor; + } + } + + protected override void UpdateDisplayValue(object value) + { + if (value is Color color && _colorDisplay != null) + { + _colorDisplay.color = color; + } + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs new file mode 100644 index 0000000..0db3182 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// Dropdown 值组件 — 处理 string / int / enum 类型的下拉选择交互 + /// 预设选项来源:元数据的 PresetOptions 或 enum 类型的枚举名 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDDropdownValue")] + public class XUIAvrgMDDropdownValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 Dropdown 组件(未设置则自动查找)")] + private Dropdown _dropdown; + + private void Awake() + { + if (_dropdown == null) _dropdown = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null || _dropdown == null) return; + + // 构建选项列表 + var options = BuildOptions(accessor.ValueType); + _dropdown.ClearOptions(); + _dropdown.AddOptions(options); + + // UI → 数据 + _dropdown.onValueChanged.AddListener(index => + { + if (index >= 0 && index < options.Count) + { + object convertedVal = ConvertOptionToValue(options[index], accessor.ValueType); + binding.WriteFromUI(convertedVal); + scheduler?.MarkUIDirty(binding); + } + }); + + // 数据 → UI + binding.OnUIDataChanged += value => + { + int idx = FindOptionIndex(value, options); + if (idx >= 0) + _dropdown.SetValueWithoutNotify(idx); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + int initialIdx = FindOptionIndex(initialValue, options); + if (initialIdx >= 0) + _dropdown.SetValueWithoutNotify(initialIdx); + } + + private List BuildOptions(System.Type valueType) + { + // 1. 从元数据 PresetOptions 获取 + if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) + return BoundMetaData.PresetOptions.ToList(); + + // 2. enum 类型:自动提取枚举名 + if (valueType != null && valueType.IsEnum) + return System.Enum.GetNames(valueType).ToList(); + + // 3. 回退 + return new List(); + } + + private int FindOptionIndex(object value, List options) + { + if (value == null || options.Count == 0) return -1; + + string valueStr = value.ToString(); + for (int i = 0; i < options.Count; i++) + { + if (options[i] == valueStr) return i; + } + return -1; + } + + private static object ConvertOptionToValue(string option, System.Type targetType) + { + if (targetType == typeof(string)) return option; + if (targetType == typeof(int) && int.TryParse(option, out int intVal)) return intVal; + if (targetType != null && targetType.IsEnum) + return System.Enum.Parse(targetType, option); + return option; + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs new file mode 100644 index 0000000..a66d113 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs @@ -0,0 +1,67 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// InputField 值组件 — 处理 string / float / int 类型的文本输入交互 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDInputFieldValue")] + public class XUIAvrgMDInputFieldValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 InputField 组件(未设置则自动查找)")] + private InputField _inputField; + + private void Awake() + { + if (_inputField == null) _inputField = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null || _inputField == null) return; + + // 设置输入类型 + if (accessor.ValueType == typeof(float) || accessor.ValueType == typeof(int) + || accessor.ValueType == typeof(double)) + { + _inputField.contentType = InputField.ContentType.DecimalNumber; + } + + // UI → 数据 + _inputField.onEndEdit.AddListener(val => + { + object convertedVal = ConvertStringToValue(val, accessor.ValueType); + binding.WriteFromUI(convertedVal); + scheduler?.MarkUIDirty(binding); + }); + + // 数据 → UI + binding.OnUIDataChanged += value => + { + _inputField.SetTextWithoutNotify(accessor.FormatDisplay(value)); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + _inputField.SetTextWithoutNotify(accessor.FormatDisplay(initialValue)); + } + + private static object ConvertStringToValue(string s, Type targetType) + { + if (targetType == typeof(string)) return s; + if (targetType == typeof(float) && float.TryParse(s, out float fVal)) return fVal; + if (targetType == typeof(int) && int.TryParse(s, out int iVal)) return iVal; + if (targetType == typeof(double) && double.TryParse(s, out double dVal)) return dVal; + return s; + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs new file mode 100644 index 0000000..f2b69c1 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs @@ -0,0 +1,57 @@ +using UnityEngine; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 标签值组件 — 处理只读文本的纯展示 + /// 适用于 Label / Context / Unit / ReadOnly 等展示型 Tag + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDLabelValue")] + public class XUIAvrgMDLabelValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 Text 组件(未设置则自动查找)")] + private UnityEngine.UI.Text _uiText; + + [SerializeField] + [Tooltip("关联的 TMP_Text 组件(未设置则自动查找)")] + private TMPro.TMP_Text _tmpText; + + private void Awake() + { + if (_uiText == null) _uiText = GetComponent(); + if (_tmpText == null) _tmpText = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null) return; + + // 数据 → UI + binding.OnUIDataChanged += value => + { + SetText(value); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + SetText(initialValue); + } + + private void SetText(object value) + { + string displayStr = value?.ToString() ?? ""; + + if (_tmpText != null) + _tmpText.text = displayStr; + else if (_uiText != null) + _uiText.text = displayStr; + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs new file mode 100644 index 0000000..a90b31a --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs @@ -0,0 +1,86 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// Slider 值组件 — 处理 float / int / double 类型的滑块交互 + /// 适用于 Shader Range 属性或标记了 [XuiavrgFieldTag(Slider)] 的字段 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDSliderValue")] + public class XUIAvrgMDSliderValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 Slider 组件(未设置则自动查找)")] + private Slider _slider; + + [SerializeField] + [Tooltip("值显示标签(可选,未设置则不更新)")] + private TMPro.TMP_Text _valueLabel; + + private void Awake() + { + if (_slider == null) _slider = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null || _slider == null) return; + + // 设置 Slider 参数 + _slider.wholeNumbers = accessor.ValueType == typeof(int); + + if (BoundMetaData != null) + { + if (BoundMetaData.RangeMin.HasValue) + _slider.minValue = BoundMetaData.RangeMin.Value; + if (BoundMetaData.RangeMax.HasValue) + _slider.maxValue = BoundMetaData.RangeMax.Value; + } + + // UI → 数据 + _slider.onValueChanged.AddListener(val => + { + object convertedVal = ConvertValueForAccessor(val, accessor.ValueType); + binding.WriteFromUI(convertedVal); + scheduler?.MarkUIDirty(binding); + + if (_valueLabel != null) + _valueLabel.text = accessor.FormatDisplay(convertedVal); + }); + + // 数据 → UI + binding.OnUIDataChanged += value => + { + float sliderVal = Convert.ToSingle(value); + _slider.SetValueWithoutNotify(sliderVal); + + if (_valueLabel != null) + _valueLabel.text = accessor.FormatDisplay(value); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + if (initialValue != null) + { + _slider.SetValueWithoutNotify(Convert.ToSingle(initialValue)); + if (_valueLabel != null) + _valueLabel.text = accessor.FormatDisplay(initialValue); + } + } + + private static object ConvertValueForAccessor(float val, Type targetType) + { + if (targetType == typeof(int)) return (int)val; + if (targetType == typeof(double)) return (double)val; + return val; + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs new file mode 100644 index 0000000..b05f0d7 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs @@ -0,0 +1,63 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 纹理值组件 — 处理 Texture 类型的预览显示 + /// 通过 RawImage.texture 展示当前纹理 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDTextureValue")] + public class XUIAvrgMDTextureValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("用于展示纹理的 RawImage 组件(未设置则自动查找)")] + private RawImage _textureDisplay; + + [SerializeField] + [Tooltip("纹理名称标签(可选,用于显示纹理资源名)")] + private TMPro.TMP_Text _nameLabel; + + private void Awake() + { + if (_textureDisplay == null) _textureDisplay = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null) return; + + // 数据 → UI + binding.OnUIDataChanged += value => + { + if (value is Texture tex && _textureDisplay != null) + { + _textureDisplay.texture = tex; + } + + if (_nameLabel != null) + { + _nameLabel.text = value is Texture t && t != null ? t.name : "None"; + } + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + if (initialValue is Texture initTex && _textureDisplay != null) + { + _textureDisplay.texture = initTex; + } + + if (_nameLabel != null) + { + _nameLabel.text = initialValue is Texture t && t != null ? t.name : "None"; + } + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs new file mode 100644 index 0000000..01f00f8 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// ToggleGroup 值组件 — 处理 string / int / enum 类型的单选组交互 + /// 适用于标记了 [XuiavrgFieldToggleGroupValue] 的字段 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDToggleGroupValue")] + public class XUIAvrgMDToggleGroupValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 ToggleGroup 组件(未设置则自动查找)")] + private ToggleGroup _toggleGroup; + + private List _toggles = new List(); + private List _optionLabels = new List(); + + private void Awake() + { + if (_toggleGroup == null) _toggleGroup = GetComponent(); + if (_toggleGroup != null) + { + _toggles = _toggleGroup.GetComponentsInChildren(true).ToList(); + } + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null || _toggleGroup == null) return; + + // 构建选项 + BuildOptions(accessor.ValueType); + + // 为每个 Toggle 注册事件 + for (int i = 0; i < _toggles.Count && i < _optionLabels.Count; i++) + { + int idx = i; + _toggles[i].onValueChanged.AddListener(isOn => + { + if (isOn) + { + object convertedVal = ConvertOptionToValue(_optionLabels[idx], accessor.ValueType); + binding.WriteFromUI(convertedVal); + scheduler?.MarkUIDirty(binding); + } + }); + } + + // 数据 → UI + binding.OnUIDataChanged += value => + { + string valueStr = value?.ToString(); + for (int i = 0; i < _toggles.Count && i < _optionLabels.Count; i++) + { + if (_optionLabels[i] == valueStr) + { + _toggles[i].SetIsOnWithoutNotify(true); + return; + } + } + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + string initStr = initialValue?.ToString(); + for (int i = 0; i < _toggles.Count && i < _optionLabels.Count; i++) + { + if (_optionLabels[i] == initStr) + { + _toggles[i].SetIsOnWithoutNotify(true); + break; + } + } + } + + private void BuildOptions(System.Type valueType) + { + _optionLabels.Clear(); + + // 1. 从元数据 PresetOptions + if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) + { + _optionLabels.AddRange(BoundMetaData.PresetOptions); + return; + } + + // 2. enum 类型 + if (valueType != null && valueType.IsEnum) + { + _optionLabels.AddRange(System.Enum.GetNames(valueType)); + return; + } + } + + private static object ConvertOptionToValue(string option, System.Type targetType) + { + if (targetType == typeof(string)) return option; + if (targetType == typeof(int) && int.TryParse(option, out int intVal)) return intVal; + if (targetType != null && targetType.IsEnum) + return System.Enum.Parse(targetType, option); + return option; + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs new file mode 100644 index 0000000..84927d0 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs @@ -0,0 +1,52 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// Toggle 值组件 — 处理 bool 类型的开关交互 + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDToggleValue")] + public class XUIAvrgMDToggleValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("关联的 Toggle 组件(未设置则自动查找)")] + private Toggle _toggle; + + private void Awake() + { + if (_toggle == null) _toggle = GetComponent(); + } + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null || _toggle == null) return; + + // 只读模式下禁用交互 + _toggle.interactable = !accessor.IsReadOnly; + + // UI → 数据 + _toggle.onValueChanged.AddListener(val => + { + binding.WriteFromUI(val); + scheduler?.MarkUIDirty(binding); + }); + + // 数据 → UI + binding.OnUIDataChanged += value => + { + _toggle.SetIsOnWithoutNotify(value is bool b && b); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + _toggle.SetIsOnWithoutNotify(initialValue is bool bv && bv); + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs new file mode 100644 index 0000000..c80d324 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs @@ -0,0 +1,137 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 向量值组件 — 处理 Vector2 / Vector3 / Vector4 类型的多字段输入 + /// 子节点按 BindingSlots 绑定各分量(如 "x", "y", "z", "w") + /// + [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDVectorValue")] + public class XUIAvrgMDVectorValue : XUIAvrgMDValue + { + [SerializeField] + [Tooltip("X 分量 InputField(可选,也可通过 BindingSlots 的子节点绑定)")] + private InputField _inputX; + + [SerializeField] + [Tooltip("Y 分量 InputField")] + private InputField _inputY; + + [SerializeField] + [Tooltip("Z 分量 InputField(Vector3/Vector4 时使用)")] + private InputField _inputZ; + + [SerializeField] + [Tooltip("W 分量 InputField(Vector4 时使用)")] + private InputField _inputW; + + public override void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null) return; + + // 根据向量类型启用/禁用对应分量 + int componentCount = GetComponentCount(accessor.ValueType); + SetInputActive(_inputX, 0, componentCount); + SetInputActive(_inputY, 1, componentCount); + SetInputActive(_inputZ, 2, componentCount); + SetInputActive(_inputW, 3, componentCount); + + // 为每个输入注册事件 + BindInputField(_inputX, 0, accessor, binding, scheduler); + BindInputField(_inputY, 1, accessor, binding, scheduler); + BindInputField(_inputZ, 2, accessor, binding, scheduler); + BindInputField(_inputW, 3, accessor, binding, scheduler); + + // 数据 → UI + binding.OnUIDataChanged += value => + { + UpdateAllInputs(value); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + UpdateAllInputs(initialValue); + } + + private void BindInputField(InputField input, int componentIndex, + IAvrgValueAccessor accessor, AvrgMvvmBinding binding, AvrgMvvmScheduler scheduler) + { + if (input == null) return; + + input.onEndEdit.AddListener(val => + { + if (float.TryParse(val, out float num)) + { + var currentVal = accessor.GetValue(); + var modified = SetVectorComponent(currentVal, componentIndex, num); + binding.WriteFromUI(modified); + scheduler?.MarkUIDirty(binding); + } + }); + } + + private void UpdateAllInputs(object value) + { + if (value == null) return; + + if (_inputX != null && _inputX.gameObject.activeSelf) + _inputX.SetTextWithoutNotify(GetVectorComponent(value, 0).ToString("F2")); + + if (_inputY != null && _inputY.gameObject.activeSelf) + _inputY.SetTextWithoutNotify(GetVectorComponent(value, 1).ToString("F2")); + + if (_inputZ != null && _inputZ.gameObject.activeSelf) + _inputZ.SetTextWithoutNotify(GetVectorComponent(value, 2).ToString("F2")); + + if (_inputW != null && _inputW.gameObject.activeSelf) + _inputW.SetTextWithoutNotify(GetVectorComponent(value, 3).ToString("F2")); + } + + private static int GetComponentCount(System.Type type) + { + if (type == typeof(Vector2)) return 2; + if (type == typeof(Vector3)) return 3; + if (type == typeof(Vector4)) return 4; + return 0; + } + + private static float GetVectorComponent(object vec, int index) + { + return vec switch + { + Vector2 v2 => index == 0 ? v2.x : v2.y, + Vector3 v3 => index == 0 ? v3.x : index == 1 ? v3.y : v3.z, + Vector4 v4 => index == 0 ? v4.x : index == 1 ? v4.y : index == 2 ? v4.z : v4.w, + _ => 0f + }; + } + + private static object SetVectorComponent(object vec, int index, float value) + { + return vec switch + { + Vector2 v2 => index == 0 ? new Vector2(value, v2.y) : new Vector2(v2.x, value), + Vector3 v3 => index == 0 ? new Vector3(value, v3.y, v3.z) + : index == 1 ? new Vector3(v3.x, value, v3.z) : new Vector3(v3.x, v3.y, value), + Vector4 v4 => index == 0 ? new Vector4(value, v4.y, v4.z, v4.w) + : index == 1 ? new Vector4(v4.x, value, v4.z, v4.w) + : index == 2 ? new Vector4(v4.x, v4.y, value, v4.w) + : new Vector4(v4.x, v4.y, v4.z, value), + _ => vec + }; + } + + private static void SetInputActive(InputField input, int index, int componentCount) + { + if (input != null) + input.gameObject.SetActive(index < componentCount); + } + } +} diff --git a/Runtime/OverrideUI/XUIAvrgMDValue.cs b/Runtime/OverrideUI/XUIAvrgMDValue.cs index 8567d35..01be1f9 100644 --- a/Runtime/OverrideUI/XUIAvrgMDValue.cs +++ b/Runtime/OverrideUI/XUIAvrgMDValue.cs @@ -4,14 +4,14 @@ using UnityEngine; namespace XericUI.ReflectionUIGenerator { /// - /// MDVM 值组件 — 统一 UI 脚本组件 - /// 替换所有 XericUIrAttributeXXX 作为预制体上的标记组件 + /// MDVM 值组件基类 — 预制体上的标记组件 /// /// 职责: /// 1. 提供 EntryTag/TagIndex 供 XuiFieldTempEntry 匹配 - /// 2. 持有绑定的 AvrgMemberMetaData 引用 - /// 3. 初始化时广播元数据到所有子节点上的 IXUIAvrgMDValueStyle - /// 4. 定义绑定槽(BindingSlots),供拆分属性子绑定匹配 + /// 2. 持有绑定的元数据引用 + /// 3. 通过 virtual BindToAccessor() 让子类自行管理 UI 交互事件和显示更新 + /// 4. 初始化时广播元数据到所有子节点上的 IXUIAvrgMDValueStyle + /// 5. 定义绑定槽(BindingSlots),供拆分属性子绑定匹配 /// [AddComponentMenu("Xeric Library/UI Action Vessel/XUIAvrgMDValue")] public class XUIAvrgMDValue : MonoBehaviour @@ -44,7 +44,13 @@ namespace XericUI.ReflectionUIGenerator private List _bindingSlots = new List(); /// 绑定的元数据引用 - public AvrgMemberMetaData BoundMetaData { get; private set; } + public AvrgMemberMetaData BoundMetaData { get; protected set; } + + /// 当前关联的值访问器 + public IAvrgValueAccessor CurrentAccessor { get; protected set; } + + /// 当前关联的 MVVM 绑定 + public AvrgMvvmBinding CurrentBinding { get; protected set; } public XuiavrgFieldTag EntryTag => _entryTag; public int TagIndex => _tagIndex; @@ -52,6 +58,60 @@ namespace XericUI.ReflectionUIGenerator /// 只读访问绑定槽列表 public IReadOnlyList AvailableBindingSlots => _bindingSlots; + /// + /// 由 XuiFieldTempEntry 调用,将值访问器和 MVVM 绑定连接到此 UI 组件 + /// + /// 基类实现提供一个默认的通用绑定逻辑(适用于 Label/Context/Unit 等简单展示型 Tag)。 + /// 子类(如 SliderValue, DropdownValue)应覆写此方法以处理其专属 UI 交互。 + /// + /// 值访问器 — 提供统一的数据读写 + /// MVVM 绑定 — 提供脏标记和 UI→数据通信通道 + /// MVVM 调度器 — 可空,用于标记脏状态 + public virtual void BindToAccessor( + IAvrgValueAccessor accessor, + AvrgMvvmBinding binding, + AvrgMvvmScheduler scheduler) + { + CurrentAccessor = accessor; + CurrentBinding = binding; + + if (binding == null || accessor == null) return; + + // 默认:数据 → UI 单向同步 + binding.OnUIDataChanged += value => + { + UpdateDisplayValue(value); + }; + + // 首次同步 + var initialValue = accessor.GetValue(); + UpdateDisplayValue(initialValue); + } + + /// + /// 更新 UI 显示值(基类默认仅查找 Text/TMP_Text 设置文本) + /// 子类可覆写以实现专属更新逻辑(如 Slider.value, Image.color 等) + /// + protected virtual void UpdateDisplayValue(object value) + { + if (value == null) return; + + // 尝试 Text + var uiText = GetComponent(); + if (uiText != null) + { + uiText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); + return; + } + + // 尝试 TMP_Text + var tmpText = GetComponent(); + if (tmpText != null) + { + tmpText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); + } + } + /// /// 由绑定系统调用,传递元数据并广播到 IXUIAvrgMDValueStyle 子组件 /// From 3fbb82f40ed556587a397a753be98fe61eb602c3 Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sat, 25 Jul 2026 22:47:14 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=88=86=E7=A6=BB=E5=85=B7=E4=BD=93?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=BB=84=E4=BB=B6=EF=BC=8C=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E8=A1=A8=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scripts/Accessors.meta | 8 ++ .../MaterialPropertyAccessor.cs.meta | 11 ++ .../Accessors/ReflectionValueAccessor.cs | 2 +- .../Accessors/ReflectionValueAccessor.cs.meta | 11 ++ .../XuiavrgFieldDropdownValueAttribute.cs | 5 + ...XuiavrgFieldDropdownValueAttribute.cs.meta | 11 ++ .../XuiavrgFieldToggleGroupValueAttribute.cs | 5 + ...avrgFieldToggleGroupValueAttribute.cs.meta | 11 ++ .../XuiavrgStyleNamespaceAttribute.cs | 33 +++++ .../XuiavrgStyleNamespaceAttribute.cs.meta | 11 ++ .../Scripts/AvrgMemberMetaData.cs | 6 + .../Scripts/Core.meta | 8 ++ .../Scripts/Core/IAvrgValueAccessor.cs.meta | 11 ++ .../Scripts/XuiavrgInspectorManager.cs | 21 ++- .../Scripts/XuiavrgUtilities.cs | 21 ++- Runtime/OverrideUI/AvrgValues.meta | 8 ++ .../AvrgValues/XUIAvrgMDColorValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDDropdownValue.cs | 11 +- .../AvrgValues/XUIAvrgMDDropdownValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDInputFieldValue.cs | 89 +++++++++++- .../XUIAvrgMDInputFieldValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDLabelValue.cs | 12 +- .../AvrgValues/XUIAvrgMDLabelValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDSliderValue.cs | 82 +++++++++++ .../AvrgValues/XUIAvrgMDSliderValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDTextureValue.cs | 6 + .../AvrgValues/XUIAvrgMDTextureValue.cs.meta | 11 ++ .../XUIAvrgMDToggleGroupValue.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDToggleValue.cs | 47 ++++++- .../AvrgValues/XUIAvrgMDToggleValue.cs.meta | 11 ++ .../{ => AvrgValues}/XUIAvrgMDValue.cs | 133 +++++++++++++++--- .../{ => AvrgValues}/XUIAvrgMDValue.cs.meta | 0 .../AvrgValues/XUIAvrgMDVectorValue.cs | 54 +++++++ .../AvrgValues/XUIAvrgMDVectorValue.cs.meta | 11 ++ .../{ => AvrgValues}/XuiAvrgMDGroup.cs | 0 .../{ => AvrgValues}/XuiAvrgMDGroup.cs.meta | 0 .../AvrgValues/XuiavrgStyleDefaults.cs | 53 +++++++ .../AvrgValues/XuiavrgStyleDefaults.cs.meta | 11 ++ 38 files changed, 742 insertions(+), 38 deletions(-) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs.meta create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs.meta rename Runtime/OverrideUI/{ => AvrgValues}/XUIAvrgMDValue.cs (52%) rename Runtime/OverrideUI/{ => AvrgValues}/XUIAvrgMDValue.cs.meta (100%) create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs.meta rename Runtime/OverrideUI/{ => AvrgValues}/XuiAvrgMDGroup.cs (100%) rename Runtime/OverrideUI/{ => AvrgValues}/XuiAvrgMDGroup.cs.meta (100%) create mode 100644 Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs create mode 100644 Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors.meta new file mode 100644 index 0000000..0799b8b --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a12c0014da358274285d451595dcf212 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs.meta new file mode 100644 index 0000000..94483aa --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 174ea317a2a5e8e44a0d96c6d7e43bd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs index f4bbc76..51290b3 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs @@ -26,7 +26,7 @@ namespace XericUI.ReflectionUIGenerator _ => typeof(object) }; - IsReadOnly = memberInfo is PropertyInfo pi && !pi.CanWrite; + IsReadOnly = memberInfo is PropertyInfo pi2 && !pi2.CanWrite; } public object GetValue() diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs.meta new file mode 100644 index 0000000..517d55f --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74ef75bd607ec5242a6bb9bb985bf436 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs index 1df7fce..a29985d 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs @@ -23,6 +23,11 @@ namespace XericUI.ReflectionUIGenerator { Options = options ?? Array.Empty(); } + public XuiavrgFieldDropdownValueAttribute(PresetOptionsMergeMode mergeMode, params string[] options) + { + MergeMode = mergeMode; + Options = options ?? Array.Empty(); + } public void BindMetaData(AvrgMemberMetaData metaData) { diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs.meta new file mode 100644 index 0000000..0c26499 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 917ba2bd8d59cce469d2c90b2201c529 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs index 2affa9e..c5e210f 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs @@ -23,6 +23,11 @@ namespace XericUI.ReflectionUIGenerator { Options = options ?? Array.Empty(); } + public XuiavrgFieldToggleGroupValueAttribute(PresetOptionsMergeMode mergeMode, params string[] options) + { + MergeMode = mergeMode; + Options = options ?? Array.Empty(); + } public void BindMetaData(AvrgMemberMetaData metaData) { diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs.meta new file mode 100644 index 0000000..d456377 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6dac053ad1c4ab043a815cb9b3c64d06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs new file mode 100644 index 0000000..0715e8b --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs @@ -0,0 +1,33 @@ +using System; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 样式命名空间特性 — 为元数据指定 SuperStyleSheet 查询时的命名空间。 + /// + /// 可标记在类上(作为该类型所有字段的默认命名空间), + /// 也可标记在字段/属性上(覆盖单个字段的命名空间)。 + /// + /// 类级标记先应用(设置默认值),字段级标记后应用(可覆盖), + /// 因此字段级 [XuiavrgStyleNamespace] 优先于类级。 + /// + /// 默认命名空间为 "default"。 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public class XuiavrgStyleNamespaceAttribute : Attribute, IXuiavrgAttribute + { + public readonly string StyleNamespace; + + /// SuperStyleSheet 命名空间名称(如 "my-theme") + public XuiavrgStyleNamespaceAttribute(string styleNamespace) + { + StyleNamespace = styleNamespace; + } + + public void BindMetaData(AvrgMemberMetaData metaData) + { + if (metaData != null) + metaData.StyleNamespace = StyleNamespace; + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs.meta new file mode 100644 index 0000000..79d5599 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgStyleNamespaceAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee38b82f1ec947d4da1981bac698fe96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs index 6e10eaa..dcfc649 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs @@ -29,6 +29,12 @@ namespace XericUI.ReflectionUIGenerator /// 值类型 public Type ValueType { get; set; } + /// + /// 样式表命名空间(用于 SuperStyleSheet 样式查询) + /// 默认 "default"。可由类级或字段级 [XuiavrgStyleNamespace] 覆写 + /// + public string StyleNamespace { get; set; } = "default"; + // ============ UI 映射 ============ /// 功能标记 diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core.meta new file mode 100644 index 0000000..b69c681 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a2aac04058602749be2f5153db24b69 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs.meta new file mode 100644 index 0000000..15f4cc0 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6a7d2f0806496e45b383e7d26eca214 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs index 60530cb..6aae0e9 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs @@ -85,6 +85,20 @@ namespace XericUI.ReflectionUIGenerator #endregion + #region Unity 生命周期 + + private void OnEnable() + { + XuiavrgStyleDefaults.EnsureDefaults(); + } + + private void OnDestroy() + { + ClearAll(); + } + + #endregion + #region 核心生命周期 public void BuildFromMetaDataSet(AvrgMemberMetaDataSet metaDataSet, @@ -443,12 +457,5 @@ namespace XericUI.ReflectionUIGenerator _leafGroupMap.Clear(); } - - private void OnDestroy() - { - ClearAll(); - } } - - #endregion } \ No newline at end of file diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs index 5c6b17e..0a48194 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgUtilities.cs @@ -35,6 +35,11 @@ namespace XericUI.ReflectionUIGenerator var metaDataSet = new AvrgMemberMetaDataSet(); + // ★ 收集类级别 IXuiavrgAttribute — 作为所有字段的基础模板 + var classAttributes = type.GetCustomAttributes() + .OfType() + .ToList(); + foreach (var memberInfo in type.GetRuntimeMemberInfo()) { // 检查隐藏标记 @@ -59,16 +64,22 @@ namespace XericUI.ReflectionUIGenerator PropertyCheckKey = valueType?.FullName, }; - // 收集并执行所有 IXuiavrgAttribute - var attributes = memberInfo.GetCustomAttributes() + // ★ 第1步: 先应用类级特性(设置默认值) + foreach (var classAttr in classAttributes) + { + classAttr.BindMetaData(metaData); + } + + // ★ 第2步: 再应用字段级特性(可覆盖类级设置) + var fieldAttributes = memberInfo.GetCustomAttributes() .OfType() .ToList(); - metaData.Attributes = attributes; + metaData.Attributes = fieldAttributes; - foreach (var attr in attributes) + foreach (var fieldAttr in fieldAttributes) { - attr.BindMetaData(metaData); + fieldAttr.BindMetaData(metaData); } metaDataSet.Add(metaData); diff --git a/Runtime/OverrideUI/AvrgValues.meta b/Runtime/OverrideUI/AvrgValues.meta new file mode 100644 index 0000000..5622718 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4541939417af914bab5ce5fcc60b4b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs.meta new file mode 100644 index 0000000..02499a9 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 529d91d01ee305847a76f071fc11ad88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs index 0db3182..a62e344 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs @@ -14,11 +14,19 @@ namespace XericUI.ReflectionUIGenerator { [SerializeField] [Tooltip("关联的 Dropdown 组件(未设置则自动查找)")] +#if dUI_TextMeshPro + private TMPro.TMP_Dropdown _dropdown; +#else private Dropdown _dropdown; +#endif private void Awake() { +#if dUI_TextMeshPro + if (_dropdown == null) _dropdown = GetComponent(); +#else if (_dropdown == null) _dropdown = GetComponent(); +#endif } public override void BindToAccessor( @@ -64,15 +72,12 @@ namespace XericUI.ReflectionUIGenerator private List BuildOptions(System.Type valueType) { - // 1. 从元数据 PresetOptions 获取 if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) return BoundMetaData.PresetOptions.ToList(); - // 2. enum 类型:自动提取枚举名 if (valueType != null && valueType.IsEnum) return System.Enum.GetNames(valueType).ToList(); - // 3. 回退 return new List(); } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs.meta new file mode 100644 index 0000000..988d2ce --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0328bccdc7751da418bd32d7ba8cb0b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs index a66d113..32a7e9d 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs @@ -1,6 +1,7 @@ using System; using UnityEngine; using UnityEngine.UI; +using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -11,14 +12,48 @@ namespace XericUI.ReflectionUIGenerator public class XUIAvrgMDInputFieldValue : XUIAvrgMDValue { [SerializeField] - [Tooltip("关联的 InputField 组件(未设置则自动查找)")] + [Tooltip("关联的 InputField 组件")] +#if dUI_TextMeshPro + private TMPro.TMP_InputField _inputField; +#else private InputField _inputField; +#endif + + [Header("InputField 专属插槽")] + [SerializeField, Tooltip("增加按钮(点击按步进值 +)")] + private Button _incrementButton; + + [SerializeField, Tooltip("减少按钮(点击按步进值 -)")] + private Button _decrementButton; + + // ============ 样式表参数 ============ + private readonly StyleMember _styleStepValue = new() { StylePath = "xuiavrg/input/stepValue" }; + + // ============ 生命周期 ============ private void Awake() { +#if dUI_TextMeshPro + if (_inputField == null) _inputField = GetComponent(); +#else if (_inputField == null) _inputField = GetComponent(); +#endif } + protected override void OnEnable() + { + base.OnEnable(); + _styleStepValue.Register(); + } + + protected override void OnDisable() + { + base.OnDisable(); + _styleStepValue.Unregister(); + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -29,12 +64,25 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null || _inputField == null) return; - // 设置输入类型 +#if dUI_TextMeshPro + if (accessor.ValueType == typeof(float) || accessor.ValueType == typeof(int) + || accessor.ValueType == typeof(double)) + { + _inputField.contentType = TMPro.TMP_InputField.ContentType.DecimalNumber; + } +#else if (accessor.ValueType == typeof(float) || accessor.ValueType == typeof(int) || accessor.ValueType == typeof(double)) { _inputField.contentType = InputField.ContentType.DecimalNumber; } +#endif + + // 增减按钮 + if (_incrementButton != null) + _incrementButton.onClick.AddListener(OnIncrementClicked); + if (_decrementButton != null) + _decrementButton.onClick.AddListener(OnDecrementClicked); // UI → 数据 _inputField.onEndEdit.AddListener(val => @@ -53,6 +101,43 @@ namespace XericUI.ReflectionUIGenerator // 首次同步 var initialValue = accessor.GetValue(); _inputField.SetTextWithoutNotify(accessor.FormatDisplay(initialValue)); + + RefreshCommonNodes(); + } + + // ============ 步进 ============ + + private float GetStepValue() + { + var val = GetStyleValue(_styleStepValue); + return val?.GetValueAsFloat() ?? 1f; + } + + private void OnIncrementClicked() + { + if (CurrentAccessor?.IsReadOnly == true || _inputField == null) return; + if (!float.TryParse(_inputField.text, out float current)) return; + float step = GetStepValue(); + float newVal = current + step; + _inputField.text = CurrentAccessor.FormatDisplay( + ConvertValueForAccessor(newVal, CurrentAccessor.ValueType)); + } + + private void OnDecrementClicked() + { + if (CurrentAccessor?.IsReadOnly == true || _inputField == null) return; + if (!float.TryParse(_inputField.text, out float current)) return; + float step = GetStepValue(); + float newVal = current - step; + _inputField.text = CurrentAccessor.FormatDisplay( + ConvertValueForAccessor(newVal, CurrentAccessor.ValueType)); + } + + private static object ConvertValueForAccessor(float val, Type targetType) + { + if (targetType == typeof(int)) return (int)val; + if (targetType == typeof(double)) return (double)val; + return val; } private static object ConvertStringToValue(string s, Type targetType) diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs.meta new file mode 100644 index 0000000..3ed4498 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66e550f9b0ba63448bf6add335a317a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs index f2b69c1..b161c89 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs @@ -13,14 +13,18 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("关联的 Text 组件(未设置则自动查找)")] private UnityEngine.UI.Text _uiText; +#if dUI_TextMeshPro [SerializeField] [Tooltip("关联的 TMP_Text 组件(未设置则自动查找)")] private TMPro.TMP_Text _tmpText; +#endif private void Awake() { if (_uiText == null) _uiText = GetComponent(); +#if dUI_TextMeshPro if (_tmpText == null) _tmpText = GetComponent(); +#endif } public override void BindToAccessor( @@ -48,9 +52,15 @@ namespace XericUI.ReflectionUIGenerator { string displayStr = value?.ToString() ?? ""; +#if dUI_TextMeshPro if (_tmpText != null) + { _tmpText.text = displayStr; - else if (_uiText != null) + return; + } +#endif + + if (_uiText != null) _uiText.text = displayStr; } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs.meta new file mode 100644 index 0000000..1907c65 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64afed910d0f0f0408c6672fa3dd6917 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs index a90b31a..f5d2cfb 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs @@ -1,6 +1,7 @@ using System; using UnityEngine; using UnityEngine.UI; +using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -15,15 +16,49 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("关联的 Slider 组件(未设置则自动查找)")] private Slider _slider; + [Header("Slider 专属插槽")] + [SerializeField, Tooltip("增加按钮(点击按步进值 +)")] + private Button _incrementButton; + + [SerializeField, Tooltip("减少按钮(点击按步进值 -)")] + private Button _decrementButton; + +#if dUI_TextMeshPro [SerializeField] [Tooltip("值显示标签(可选,未设置则不更新)")] private TMPro.TMP_Text _valueLabel; +#endif + + // ============ 样式表参数 ============ + private readonly StyleMember _styleDefaultStep = new() { StylePath = "xuiavrg/slider/defaultStep" }; + private readonly StyleMember _styleMinStep = new() { StylePath = "xuiavrg/slider/minStep" }; + private readonly StyleMember _styleMaxStep = new() { StylePath = "xuiavrg/slider/maxStep" }; + + // ============ 生命周期 ============ private void Awake() { if (_slider == null) _slider = GetComponent(); } + protected override void OnEnable() + { + base.OnEnable(); + _styleDefaultStep.Register(); + _styleMinStep.Register(); + _styleMaxStep.Register(); + } + + protected override void OnDisable() + { + base.OnDisable(); + _styleDefaultStep.Unregister(); + _styleMinStep.Unregister(); + _styleMaxStep.Unregister(); + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -45,6 +80,12 @@ namespace XericUI.ReflectionUIGenerator _slider.maxValue = BoundMetaData.RangeMax.Value; } + // 增减按钮 + if (_incrementButton != null) + _incrementButton.onClick.AddListener(OnIncrementClicked); + if (_decrementButton != null) + _decrementButton.onClick.AddListener(OnDecrementClicked); + // UI → 数据 _slider.onValueChanged.AddListener(val => { @@ -52,8 +93,10 @@ namespace XericUI.ReflectionUIGenerator binding.WriteFromUI(convertedVal); scheduler?.MarkUIDirty(binding); +#if dUI_TextMeshPro if (_valueLabel != null) _valueLabel.text = accessor.FormatDisplay(convertedVal); +#endif }); // 数据 → UI @@ -62,8 +105,10 @@ namespace XericUI.ReflectionUIGenerator float sliderVal = Convert.ToSingle(value); _slider.SetValueWithoutNotify(sliderVal); +#if dUI_TextMeshPro if (_valueLabel != null) _valueLabel.text = accessor.FormatDisplay(value); +#endif }; // 首次同步 @@ -71,9 +116,46 @@ namespace XericUI.ReflectionUIGenerator if (initialValue != null) { _slider.SetValueWithoutNotify(Convert.ToSingle(initialValue)); +#if dUI_TextMeshPro if (_valueLabel != null) _valueLabel.text = accessor.FormatDisplay(initialValue); +#endif } + + RefreshCommonNodes(); + } + + // ============ 步进值 ============ + + private float GetStepValue() + { + var val = GetStyleValue(_styleDefaultStep); + if (val != null) + { + float step = val.GetValueAsFloat(); + float min = GetStyleValue(_styleMinStep)?.GetValueAsFloat() ?? 0.001f; + float max = GetStyleValue(_styleMaxStep)?.GetValueAsFloat() ?? 1000f; + return Mathf.Clamp(step, min, max); + } + return _slider.wholeNumbers ? 1f : 0.1f; + } + + private void OnIncrementClicked() + { + if (CurrentAccessor?.IsReadOnly == true || _slider == null) return; + float step = GetStepValue(); + float current = Convert.ToSingle(CurrentAccessor.GetValue()); + float newVal = Mathf.Clamp(current + step, _slider.minValue, _slider.maxValue); + _slider.value = newVal; + } + + private void OnDecrementClicked() + { + if (CurrentAccessor?.IsReadOnly == true || _slider == null) return; + float step = GetStepValue(); + float current = Convert.ToSingle(CurrentAccessor.GetValue()); + float newVal = Mathf.Clamp(current - step, _slider.minValue, _slider.maxValue); + _slider.value = newVal; } private static object ConvertValueForAccessor(float val, Type targetType) diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs.meta new file mode 100644 index 0000000..a6ace60 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b55bbe984d79124ba933f6d7cc2232f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs index b05f0d7..35e109f 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs @@ -14,9 +14,11 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("用于展示纹理的 RawImage 组件(未设置则自动查找)")] private RawImage _textureDisplay; +#if dUI_TextMeshPro [SerializeField] [Tooltip("纹理名称标签(可选,用于显示纹理资源名)")] private TMPro.TMP_Text _nameLabel; +#endif private void Awake() { @@ -41,10 +43,12 @@ namespace XericUI.ReflectionUIGenerator _textureDisplay.texture = tex; } +#if dUI_TextMeshPro if (_nameLabel != null) { _nameLabel.text = value is Texture t && t != null ? t.name : "None"; } +#endif }; // 首次同步 @@ -54,10 +58,12 @@ namespace XericUI.ReflectionUIGenerator _textureDisplay.texture = initTex; } +#if dUI_TextMeshPro if (_nameLabel != null) { _nameLabel.text = initialValue is Texture t && t != null ? t.name : "None"; } +#endif } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs.meta new file mode 100644 index 0000000..024d534 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0655c30e41dc2884f91f4acb4666a271 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs.meta new file mode 100644 index 0000000..3a02558 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 330c7c83db7ef3141a8097a27d2cf61d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs index 84927d0..3d741cf 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs @@ -1,10 +1,12 @@ using UnityEngine; using UnityEngine.UI; +using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { /// /// Toggle 值组件 — 处理 bool 类型的开关交互 + /// 支持样式表控制开关颜色 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDToggleValue")] public class XUIAvrgMDToggleValue : XUIAvrgMDValue @@ -13,11 +15,33 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("关联的 Toggle 组件(未设置则自动查找)")] private Toggle _toggle; + // ============ 样式表参数 ============ + private readonly StyleMember _styleOnColor = new() { StylePath = "xuiavrg/toggle/onColor" }; + private readonly StyleMember _styleOffColor = new() { StylePath = "xuiavrg/toggle/offColor" }; + + // ============ 生命周期 ============ + private void Awake() { if (_toggle == null) _toggle = GetComponent(); } + protected override void OnEnable() + { + base.OnEnable(); + _styleOnColor.Register(); + _styleOffColor.Register(); + } + + protected override void OnDisable() + { + base.OnDisable(); + _styleOnColor.Unregister(); + _styleOffColor.Unregister(); + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -36,17 +60,36 @@ namespace XericUI.ReflectionUIGenerator { binding.WriteFromUI(val); scheduler?.MarkUIDirty(binding); + ApplyToggleColor(val); }); // 数据 → UI binding.OnUIDataChanged += value => { - _toggle.SetIsOnWithoutNotify(value is bool b && b); + bool isOn = value is bool b && b; + _toggle.SetIsOnWithoutNotify(isOn); + ApplyToggleColor(isOn); }; // 首次同步 var initialValue = accessor.GetValue(); - _toggle.SetIsOnWithoutNotify(initialValue is bool bv && bv); + bool initialOn = initialValue is bool bv && bv; + _toggle.SetIsOnWithoutNotify(initialOn); + ApplyToggleColor(initialOn); + + RefreshCommonNodes(); + } + + // ============ 样式颜色 ============ + + private void ApplyToggleColor(bool isOn) + { + var target = _toggle.targetGraphic; + if (target == null) return; + + var colorVal = isOn ? GetStyleValue(_styleOnColor) : GetStyleValue(_styleOffColor); + if (colorVal != null) + target.color = colorVal; } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs.meta new file mode 100644 index 0000000..b1305bd --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00fd149c9a8dacd478a5adcad14a11f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/XUIAvrgMDValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs similarity index 52% rename from Runtime/OverrideUI/XUIAvrgMDValue.cs rename to Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs index 01be1f9..6948365 100644 --- a/Runtime/OverrideUI/XUIAvrgMDValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using UnityEngine; +using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -12,6 +13,7 @@ namespace XericUI.ReflectionUIGenerator /// 3. 通过 virtual BindToAccessor() 让子类自行管理 UI 交互事件和显示更新 /// 4. 初始化时广播元数据到所有子节点上的 IXUIAvrgMDValueStyle /// 5. 定义绑定槽(BindingSlots),供拆分属性子绑定匹配 + /// 6. 管理通用 UI 插槽(标签、警告、只读、单位、值显示)+ SuperStyleSheet 样式读取 /// [AddComponentMenu("Xeric Library/UI Action Vessel/XUIAvrgMDValue")] public class XUIAvrgMDValue : MonoBehaviour @@ -31,6 +33,8 @@ namespace XericUI.ReflectionUIGenerator public XuiavrgFieldTag tag = XuiavrgFieldTag.Context; } + // ============ 匹配标记 ============ + [SerializeField] [Tooltip("功能标记,用于与 Entry 的 Tag 匹配")] private XuiavrgFieldTag _entryTag = XuiavrgFieldTag.None; @@ -43,6 +47,32 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("拆分属性子绑定槽列表 — 用于 XuiavrgSplit 的子属性匹配")] private List _bindingSlots = new List(); + // ============ 通用 UI 插槽 ============ + + [Header("通用 UI 插槽")] + [SerializeField, Tooltip("字段标签节点 — 显示字段名称 + 只读前缀。为 null 则不显示标签行")] + protected GameObject fieldLabelNode; + + [SerializeField, Tooltip("警告样式节点 — 验证失败时 SetActive(true),颜色由样式表控制")] + protected GameObject warningNode; + + [SerializeField, Tooltip("只读标记节点 — IsReadOnly 时 SetActive(true)")] + protected GameObject readOnlyNode; + + [SerializeField, Tooltip("单位文本节点 — 显示元数据的 UnitLabelName")] + protected GameObject unitTextNode; + + [SerializeField, Tooltip("值显示文本节点 — 可为 null(子类如 Slider/Color 不依赖文本)")] + protected GameObject valueTextNode; + + // ============ 样式表参数(路径硬编码,命名空间从 BoundMetaData.StyleNamespace 获取)============ + + private readonly StyleMember _styleStepValue = new() { StylePath = "xuiavrg/base/stepValue" }; + private readonly StyleMember _styleReadOnlyPrefix = new() { StylePath = "xuiavrg/base/readOnlyPrefix" }; + private readonly StyleMember _styleWarningColor = new() { StylePath = "xuiavrg/base/warningColor" }; + + // ============ 公共属性 ============ + /// 绑定的元数据引用 public AvrgMemberMetaData BoundMetaData { get; protected set; } @@ -58,15 +88,27 @@ namespace XericUI.ReflectionUIGenerator /// 只读访问绑定槽列表 public IReadOnlyList AvailableBindingSlots => _bindingSlots; + // ============ 生命周期 ============ + + protected virtual void OnEnable() + { + _styleStepValue.Register(); + _styleReadOnlyPrefix.Register(); + _styleWarningColor.Register(); + } + + protected virtual void OnDisable() + { + _styleStepValue.Unregister(); + _styleReadOnlyPrefix.Unregister(); + _styleWarningColor.Unregister(); + } + + // ============ 核心绑定接口 ============ + /// /// 由 XuiFieldTempEntry 调用,将值访问器和 MVVM 绑定连接到此 UI 组件 - /// - /// 基类实现提供一个默认的通用绑定逻辑(适用于 Label/Context/Unit 等简单展示型 Tag)。 - /// 子类(如 SliderValue, DropdownValue)应覆写此方法以处理其专属 UI 交互。 /// - /// 值访问器 — 提供统一的数据读写 - /// MVVM 绑定 — 提供脏标记和 UI→数据通信通道 - /// MVVM 调度器 — 可空,用于标记脏状态 public virtual void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -86,6 +128,9 @@ namespace XericUI.ReflectionUIGenerator // 首次同步 var initialValue = accessor.GetValue(); UpdateDisplayValue(initialValue); + + // 刷新通用插槽 + RefreshCommonNodes(); } /// @@ -96,19 +141,19 @@ namespace XericUI.ReflectionUIGenerator { if (value == null) return; - // 尝试 Text - var uiText = GetComponent(); - if (uiText != null) - { - uiText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); - return; - } - - // 尝试 TMP_Text +#if dUI_TextMeshPro var tmpText = GetComponent(); if (tmpText != null) { tmpText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); + return; + } +#endif + + var uiText = GetComponent(); + if (uiText != null) + { + uiText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); } } @@ -121,16 +166,70 @@ namespace XericUI.ReflectionUIGenerator if (metaData == null) return; - // 查找子节点上的所有样式扩展组件并广播元数据 + // 广播元数据到子节点样式组件 var styles = GetComponentsInChildren(true); foreach (var style in styles) { - // 跳过自身(XUIAvrgMDValue 不是 IXUIAvrgMDValueStyle) if (style is MonoBehaviour mb && mb.gameObject == gameObject) continue; style.ApplyMetaData(metaData); } } + + // ============ 样式读取 ============ + + /// + /// 从样式表获取值(自动使用 BoundMetaData.StyleNamespace) + /// + protected StyleValue GetStyleValue(StyleMember member) + { + if (member == null) return null; + string ns = BoundMetaData?.StyleNamespace ?? "default"; + return StyleManager.GetValue(ns, member.StylePath); + } + + // ============ 通用 UI 插槽刷新 ============ + + /// 刷新所有通用 UI 插槽(标签、单位、只读、警告标记) + protected void RefreshCommonNodes() + { + var meta = BoundMetaData; + if (meta == null) return; + + // 1. 字段标签节点 + if (fieldLabelNode != null) + { + string prefix = ""; + if (CurrentAccessor?.IsReadOnly == true) + { + var pv = GetStyleValue(_styleReadOnlyPrefix); + prefix = pv?.GetValueAsString() ?? "[只读] "; + } + SetNodeText(fieldLabelNode, prefix + (meta.LabelName ?? meta.MemberName)); + } + + // 2. 单位文本节点 + if (unitTextNode != null) + SetNodeText(unitTextNode, meta.UnitLabelName ?? ""); + + // 3. 只读标记节点 + if (readOnlyNode != null) + readOnlyNode.SetActive(CurrentAccessor?.IsReadOnly == true); + + // 4. warningNode 由外部验证系统触发,此方法不直接控制 + } + + /// 在节点及其子节点上查找 Text / TMP_Text 并设置文本 + protected static void SetNodeText(GameObject node, string text) + { + if (node == null) return; +#if dUI_TextMeshPro + var t = node.GetComponentInChildren(true); + if (t != null) { t.text = text; return; } +#endif + var ut = node.GetComponentInChildren(true); + if (ut != null) ut.text = text; + } } } diff --git a/Runtime/OverrideUI/XUIAvrgMDValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs.meta similarity index 100% rename from Runtime/OverrideUI/XUIAvrgMDValue.cs.meta rename to Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs.meta diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs index c80d324..470eb39 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEngine.UI; +using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -26,6 +27,25 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("W 分量 InputField(Vector4 时使用)")] private InputField _inputW; + // ============ 样式表参数 ============ + private readonly StyleMember _styleLayoutDirection = new() { StylePath = "xuiavrg/vector/layoutDirection" }; + + // ============ 生命周期 ============ + + protected override void OnEnable() + { + base.OnEnable(); + _styleLayoutDirection.Register(); + } + + protected override void OnDisable() + { + base.OnDisable(); + _styleLayoutDirection.Unregister(); + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -43,6 +63,11 @@ namespace XericUI.ReflectionUIGenerator SetInputActive(_inputZ, 2, componentCount); SetInputActive(_inputW, 3, componentCount); + // 样式表控制排列方向 + var dirVal = GetStyleValue(_styleLayoutDirection); + string dir = dirVal?.GetValueAsString() ?? "Horizontal"; + ApplyLayoutDirection(dir); + // 为每个输入注册事件 BindInputField(_inputX, 0, accessor, binding, scheduler); BindInputField(_inputY, 1, accessor, binding, scheduler); @@ -58,8 +83,37 @@ namespace XericUI.ReflectionUIGenerator // 首次同步 var initialValue = accessor.GetValue(); UpdateAllInputs(initialValue); + + RefreshCommonNodes(); } + // ============ 布局 ============ + + private void ApplyLayoutDirection(string dir) + { + var layout = GetComponent(); + if (layout == null) return; + + if (dir == "Vertical") + { + if (layout is HorizontalLayoutGroup) + { + Destroy(layout); + gameObject.AddComponent(); + } + } + else + { + if (layout is VerticalLayoutGroup) + { + Destroy(layout); + gameObject.AddComponent(); + } + } + } + + // ============ 输入绑定 ============ + private void BindInputField(InputField input, int componentIndex, IAvrgValueAccessor accessor, AvrgMvvmBinding binding, AvrgMvvmScheduler scheduler) { diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs.meta b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs.meta new file mode 100644 index 0000000..90b97a2 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 128edc3f2c56f784d8a3acca12c960b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/XuiAvrgMDGroup.cs b/Runtime/OverrideUI/AvrgValues/XuiAvrgMDGroup.cs similarity index 100% rename from Runtime/OverrideUI/XuiAvrgMDGroup.cs rename to Runtime/OverrideUI/AvrgValues/XuiAvrgMDGroup.cs diff --git a/Runtime/OverrideUI/XuiAvrgMDGroup.cs.meta b/Runtime/OverrideUI/AvrgValues/XuiAvrgMDGroup.cs.meta similarity index 100% rename from Runtime/OverrideUI/XuiAvrgMDGroup.cs.meta rename to Runtime/OverrideUI/AvrgValues/XuiAvrgMDGroup.cs.meta diff --git a/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs b/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs new file mode 100644 index 0000000..2bf6ba4 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs @@ -0,0 +1,53 @@ +using UnityEngine; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// Xuiavrg 样式默认值注入 + /// + /// 在应用启动时通过 StyleManager.AddDynamicStyle() 将 xuiavrg 各组件的 + /// 默认样式参数注入到 "default" 命名空间下。用户可在 .xsss 文件中覆写。 + /// + /// 触发时机:XuiavrgInspectorManager.OnEnable() 调用 EnsureDefaults() + /// + public static class XuiavrgStyleDefaults + { + private static bool _defaultsInjected; + private const string NS = "default"; + + /// 确保 "default" 命名空间下有所有的 xuiavrg 默认样式值(幂等) + public static void EnsureDefaults() + { + if (_defaultsInjected) return; + _defaultsInjected = true; + + // ===== 基类通用 ===== + AddIfMissing(NS, "xuiavrg/base/stepValue", 1f); + AddIfMissing(NS, "xuiavrg/base/readOnlyPrefix", "[只读] "); + AddIfMissing(NS, "xuiavrg/base/warningColor", Color.yellow); + + // ===== Slider ===== + AddIfMissing(NS, "xuiavrg/slider/defaultStep", 0.1f); + AddIfMissing(NS, "xuiavrg/slider/minStep", 0.001f); + AddIfMissing(NS, "xuiavrg/slider/maxStep", 1000f); + + // ===== InputField ===== + AddIfMissing(NS, "xuiavrg/input/stepValue", 1f); + + // ===== Toggle ===== + AddIfMissing(NS, "xuiavrg/toggle/onColor", Color.green); + AddIfMissing(NS, "xuiavrg/toggle/offColor", new Color(0.5f, 0.5f, 0.5f, 1f)); + + // ===== Vector ===== + AddIfMissing(NS, "xuiavrg/vector/layoutDirection", "Horizontal"); + + XericLibrary.Runtime.SuperStyleSheet.StyleManager.ForceRefresh(); + } + + private static void AddIfMissing(string ns, string path, object value) + { + if (XericLibrary.Runtime.SuperStyleSheet.StyleManager.HasStyle(ns, path)) return; + XericLibrary.Runtime.SuperStyleSheet.StyleManager.AddDynamicStyle(ns, path, value); + } + } +} diff --git a/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs.meta b/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs.meta new file mode 100644 index 0000000..9093073 --- /dev/null +++ b/Runtime/OverrideUI/AvrgValues/XuiavrgStyleDefaults.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d14548d93d7669346a061384defd8460 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 251b8730b977c442edd8e2b291dcb8c7a12a7e71 Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sat, 25 Jul 2026 23:38:49 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BC=98=E5=8C=96ui=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E4=BE=A7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scripts/AvrgBindingTrie.cs | 77 ++++++ .../Scripts/AvrgMemberMetaDataSet.cs | 16 ++ .../Scripts/AvrgMvvmBinding.cs | 64 ++++- .../Scripts/AvrgMvvmBindingCollection.cs | 48 +++- .../Scripts/AvrgMvvmScheduler.cs | 123 ++++++--- .../Scripts/AvrgSubPropertyAccessor.cs | 79 ++++++ .../Scripts/ValidationState.cs | 13 + .../Scripts/XuiFieldTempEntry.cs | 4 +- .../Scripts/XuiavrgInspectorManager.cs | 14 +- .../AvrgValues/XUIAvrgMDColorValue.cs | 37 +-- .../AvrgValues/XUIAvrgMDDropdownValue.cs | 61 +++-- .../AvrgValues/XUIAvrgMDInputFieldValue.cs | 71 +++-- .../AvrgValues/XUIAvrgMDLabelValue.cs | 44 +--- .../AvrgValues/XUIAvrgMDSliderValue.cs | 105 ++++---- .../AvrgValues/XUIAvrgMDTextureValue.cs | 51 ++-- .../AvrgValues/XUIAvrgMDToggleGroupValue.cs | 11 +- .../AvrgValues/XUIAvrgMDToggleValue.cs | 69 ++--- .../OverrideUI/AvrgValues/XUIAvrgMDValue.cs | 247 ++++++++++-------- .../AvrgValues/XUIAvrgMDVectorValue.cs | 84 ++---- 19 files changed, 782 insertions(+), 436 deletions(-) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs new file mode 100644 index 0000000..feeb9e1 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 绑定名称前缀树(Trie),支持通过完整路径或成员名后缀快速查找 AvrgMvvmBinding。 + /// + /// 使用场景: + /// - Find("XericUI.MyData.speed") → 完整路径精确命中 + /// - FindByMemberName("speed") → 沿 Trie 叶节点查找末段匹配 + /// + /// 在 BuildMDVM 时构建,绑定到 AvrgMvvmBindingCollection。 + /// + internal class AvrgBindingTrie + { + private class Node + { + public AvrgMvvmBinding Binding; + public readonly Dictionary Children = new(); + } + + private readonly Node _root = new(); + private readonly Dictionary _memberNameIndex = new(); + + public void Insert(string fullPath, AvrgMvvmBinding binding) + { + if (string.IsNullOrEmpty(fullPath) || binding == null) return; + + var parts = fullPath.Split('.'); + var current = _root; + foreach (var part in parts) + { + if (!current.Children.TryGetValue(part, out var child)) + { + child = new Node(); + current.Children[part] = child; + } + current = child; + } + current.Binding = binding; + + // 同时建成员名索引(末段) + string memberName = parts[parts.Length - 1]; + if (!_memberNameIndex.ContainsKey(memberName)) + _memberNameIndex[memberName] = binding; + } + + /// 通过完整路径精确查找(如 "XericUI.MyData.speed") + public AvrgMvvmBinding Find(string fullPath) + { + if (string.IsNullOrEmpty(fullPath)) return null; + + var parts = fullPath.Split('.'); + var current = _root; + foreach (var part in parts) + { + if (!current.Children.TryGetValue(part, out current)) + return null; + } + return current?.Binding; + } + + /// 通过成员名查找(如 "speed")— O(1) 直接索引 + public AvrgMvvmBinding FindByMemberName(string memberName) + { + _memberNameIndex.TryGetValue(memberName, out var binding); + return binding; + } + + /// 清除所有索引 + public void Clear() + { + _root.Children.Clear(); + _memberNameIndex.Clear(); + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs index 56be430..9c1fde4 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs @@ -91,6 +91,12 @@ namespace XericUI.ReflectionUIGenerator bindingCollection.SourceMetaDataSet = this; var scheduler = new AvrgMvvmScheduler(resolverConfig); + // ★ 构建前缀树索引 + var trie = new AvrgBindingTrie(); + string typePrefix = businessTarget != null + ? businessTarget.GetType().FullName + "." + : ""; + foreach (var metaData in _metaDataList) { if (metaData.AccessorFactory == null) continue; @@ -137,9 +143,14 @@ namespace XericUI.ReflectionUIGenerator string subPath = subMetaData.PropertyPath; if (string.IsNullOrEmpty(subPath)) continue; + // 创建子属性访问器 + var subAccessor = new AvrgSubPropertyAccessor( + accessor, subPath, subMetaData); + var subBinding = new AvrgMvvmBinding { MetaData = subMetaData, + Accessor = subAccessor, ReadFunc = () => ReadSubProperty(accessor.GetValue(), subPath), WriteAction = (val) => { @@ -157,8 +168,13 @@ namespace XericUI.ReflectionUIGenerator // 注册到调度器 scheduler.Register(binding); bindingCollection.Add(binding); + + // ★ 插入前缀树(完整路径: Type.FullName.MemberName) + string fullPath = typePrefix + metaData.MemberName; + trie.Insert(fullPath, binding); } + bindingCollection.SetTrie(trie); bindingCollection.AttachScheduler(scheduler); return bindingCollection; } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs index c8faa0f..757e32b 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs @@ -45,21 +45,75 @@ namespace XericUI.ReflectionUIGenerator /// 数据侧写入的缓存值 public object DataCachedValue { get; set; } + // ============ 验证状态 ============ + + private ValidationState _validationState = ValidationState.Normal; + + /// 当前验证状态 + public ValidationState CurrentValidationState => _validationState; + + /// 验证状态变更事件 — UI 组件订阅以切换样式 + public event Action OnValidationStateChanged; + + /// + /// 校验值是否满足元数据约束 + /// 返回:Normal(通过)、Warning(放行但警告)、Invalid(驳回) + /// + public ValidationState ValidateValue(object value) + { + if (MetaData == null) return ValidationState.Normal; + + // 1. 空值检查(Required) + if (MetaData.Required && (value == null || (value is string s && string.IsNullOrEmpty(s)))) + return ValidationState.Invalid; + + // 2. 范围检查(RangeMin/RangeMax) + if (value is IConvertible) + { + float num = Convert.ToSingle(value); + + if (MetaData.RangeMin.HasValue && num < MetaData.RangeMin.Value) + return ValidationState.Invalid; + + if (MetaData.RangeMax.HasValue && num > MetaData.RangeMax.Value) + return ValidationState.Invalid; + } + + return ValidationState.Normal; + } + + /// 更新验证状态并触发回调(仅状态变化时触发) + internal void SetValidationState(ValidationState newState) + { + if (_validationState != newState) + { + _validationState = newState; + OnValidationStateChanged?.Invoke(_validationState); + } + } + + /// 重置验证状态为 Normal(不触发事件) + internal void ResetValidationState() + { + _validationState = ValidationState.Normal; + } + // ============ UI 绑定 ============ - /// UI 值变更回调委托 — UI 组件订阅此回调以更新显示 - public Action OnUIDataChanged; + /// UI 值变更回调委托 — UI 组件订阅此回调以更新显示(附带验证状态) + public Action OnUIDataChanged; - /// 通知 UI 更新值 - public void NotifyUIUpdate(object value) + /// 通知 UI 更新值(带验证状态) + public void NotifyUIUpdate(object value, ValidationState state) { - OnUIDataChanged?.Invoke(value); + OnUIDataChanged?.Invoke(value, state); } /// 清除所有 UI 变更订阅 public void ClearUIEvent() { OnUIDataChanged = null; + OnValidationStateChanged = null; } // ============ 便捷方法 ============ diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBindingCollection.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBindingCollection.cs index 39a5a0d..5f6f265 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBindingCollection.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBindingCollection.cs @@ -4,12 +4,13 @@ using System.Collections.Generic; namespace XericUI.ReflectionUIGenerator { /// - /// 绑定集合 — 统一管理所有 AvrgMvvmBinding + /// 绑定集合 — 统一管理所有 AvrgMvvmBinding,提供字符串索引器和前缀树快速查找 /// public class AvrgMvvmBindingCollection : IEnumerable { private List _bindings = new List(); private AvrgMvvmScheduler _scheduler; + private AvrgBindingTrie _trie; /// /// 绑定的元数据集引用(可选) @@ -21,6 +22,11 @@ namespace XericUI.ReflectionUIGenerator /// public int Count => _bindings.Count; + /// + /// 调度器引用(在 BuildMDVM 时设置) + /// + public AvrgMvvmScheduler Scheduler => _scheduler; + /// /// 添加绑定 /// @@ -29,16 +35,53 @@ namespace XericUI.ReflectionUIGenerator _bindings.Add(binding); } + /// + /// 设置前缀树索引(由 BuildMDVM 构建后注入) + /// + internal void SetTrie(AvrgBindingTrie trie) + { + _trie = trie; + } + /// /// 获取指定索引的绑定 /// public AvrgMvvmBinding this[int index] => _bindings[index]; /// - /// 获取指定成员名称的绑定 + /// 通过成员名访问绑定(如 bindingCollection["speed"]) + /// get: 从数据源读取当前值 + /// set: 走完整的 MVVM 管线:WriteFromData → MarkDataDirty → Flush → UI 同步 + /// + public object this[string memberName] + { + get + { + var binding = FindByMemberName(memberName); + return binding?.ReadFromDataSource(); + } + set + { + var binding = FindByMemberName(memberName); + if (binding != null && _scheduler != null) + { + binding.WriteFromData(value); + _scheduler.MarkDataDirty(binding); + _scheduler.Flush(); + } + } + } + + /// + /// 获取指定成员名称的绑定(优先查 Trie,回退线性查找) /// public AvrgMvvmBinding FindByMemberName(string memberName) { + if (_trie != null) + { + var found = _trie.Find(memberName) ?? _trie.FindByMemberName(memberName); + if (found != null) return found; + } return _bindings.Find(b => b.MetaData?.MemberName == memberName); } @@ -72,6 +115,7 @@ namespace XericUI.ReflectionUIGenerator public void Clear() { _bindings.Clear(); + _trie?.Clear(); _scheduler?.Clear(); } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmScheduler.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmScheduler.cs index 5f1486d..8884c9b 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmScheduler.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmScheduler.cs @@ -9,8 +9,11 @@ namespace XericUI.ReflectionUIGenerator /// /// 冲突解决策略: /// - 仅数据脏 → 通知 UI 更新 - /// - 仅 UI 脏 → 写入数据源 + /// - 仅 UI 脏 → 写入数据源(写入前校验,Invalid 驳回) /// - 双方都脏 → 走配置的冲突解决策略 + /// + /// 验证拦截:在 CommitToDataSource 前通过 Binding.ValidateValue() 校验。 + /// Invalid 值被驳回不写入,仅通知 UI 进入无效状态。 /// public class AvrgMvvmScheduler { @@ -79,20 +82,36 @@ namespace XericUI.ReflectionUIGenerator if (uiDirty && dataDirty) { - // 双方都脏 — 走冲突解决 + // 双方都脏 — 走冲突解决(内部含验证) ResolveConflict(binding); } else if (dataDirty) { - // 仅数据侧更新 → 通知 UI + // 仅数据侧更新 → 验证后通知 UI var value = binding.DataCachedValue ?? binding.ReadFromDataSource(); + var state = binding.ValidateValue(value); + binding.SetValidationState(state); + binding.CommitToDataSource(value); - binding.NotifyUIUpdate(value); + binding.NotifyUIUpdate(value, state); } else if (uiDirty) { - // 仅 UI 侧更新 → 写入数据源 - binding.CommitToDataSource(binding.UICachedValue); + // 仅 UI 侧更新 → 验证后写入数据源 + var value = binding.UICachedValue; + var state = binding.ValidateValue(value); + + if (state == ValidationState.Invalid) + { + // ★ 无效值:驳回不写入,仅通知 UI 进入无效态 + binding.SetValidationState(state); + binding.NotifyUIUpdate(value, state); + return; + } + + binding.SetValidationState(state); + binding.CommitToDataSource(value); + // UI 是发起方,不再通知自身(但验证状态变更需通知) } } @@ -106,8 +125,10 @@ namespace XericUI.ReflectionUIGenerator var resolvedValue = OnConflictDetected.Invoke(binding, DefaultStrategy); if (resolvedValue != null) { + var state = binding.ValidateValue(resolvedValue); + binding.SetValidationState(state); binding.CommitToDataSource(resolvedValue); - binding.NotifyUIUpdate(resolvedValue); + binding.NotifyUIUpdate(resolvedValue, state); return; } } @@ -120,8 +141,10 @@ namespace XericUI.ReflectionUIGenerator // 2. 检查 ResolverConfig 中的自定义策略 if (_resolverConfig != null && _resolverConfig.TryResolve(binding, out object configResolvedValue)) { + var state = binding.ValidateValue(configResolvedValue); + binding.SetValidationState(state); binding.CommitToDataSource(configResolvedValue); - binding.NotifyUIUpdate(configResolvedValue); + binding.NotifyUIUpdate(configResolvedValue, state); return; } @@ -129,38 +152,70 @@ namespace XericUI.ReflectionUIGenerator switch (DefaultStrategy) { case ConflictResolutionStrategy.PreferUI: - binding.CommitToDataSource(binding.UICachedValue); - binding.NotifyUIUpdate(binding.UICachedValue); - break; - - case ConflictResolutionStrategy.PreferData: - var dataValue = binding.DataCachedValue ?? binding.ReadFromDataSource(); - binding.CommitToDataSource(dataValue); - binding.NotifyUIUpdate(dataValue); - break; - - case ConflictResolutionStrategy.CompareValues: - // 比较UI值和数据值 - object uiVal = binding.UICachedValue; - object dataVal = binding.DataCachedValue ?? binding.ReadFromDataSource(); - - if (Equals(uiVal, dataVal)) { - // 值相同,忽略冲突 - binding.NotifyUIUpdate(dataVal); + var val = binding.UICachedValue; + var state = binding.ValidateValue(val); + if (state == ValidationState.Invalid) + { + binding.SetValidationState(state); + binding.NotifyUIUpdate(val, state); + return; + } + binding.SetValidationState(state); + binding.CommitToDataSource(val); + binding.NotifyUIUpdate(val, state); break; } - // 值不同,回退到 PreferUI - binding.CommitToDataSource(binding.UICachedValue); - binding.NotifyUIUpdate(binding.UICachedValue); - Debug.LogWarning($"[AvrgMvvmScheduler] 值冲突(CompareValues回退PreferUI): {binding.MetaData?.MemberName}"); - break; + case ConflictResolutionStrategy.PreferData: + { + var dataValue = binding.DataCachedValue ?? binding.ReadFromDataSource(); + var state = binding.ValidateValue(dataValue); + binding.SetValidationState(state); + binding.CommitToDataSource(dataValue); + binding.NotifyUIUpdate(dataValue, state); + break; + } + + case ConflictResolutionStrategy.CompareValues: + { + object uiVal = binding.UICachedValue; + object dataVal = binding.DataCachedValue ?? binding.ReadFromDataSource(); + + if (Equals(uiVal, dataVal)) + { + // 值相同,忽略冲突 + var state2 = binding.ValidateValue(dataVal); + binding.SetValidationState(state2); + binding.NotifyUIUpdate(dataVal, state2); + break; + } + + // 值不同,回退到 PreferUI + var val = binding.UICachedValue; + var state = binding.ValidateValue(val); + if (state == ValidationState.Invalid) + { + binding.SetValidationState(state); + binding.NotifyUIUpdate(val, state); + return; + } + binding.SetValidationState(state); + binding.CommitToDataSource(val); + binding.NotifyUIUpdate(val, state); + Debug.LogWarning($"[AvrgMvvmScheduler] 值冲突(CompareValues回退PreferUI): {binding.MetaData?.MemberName}"); + break; + } default: - binding.CommitToDataSource(binding.UICachedValue); - binding.NotifyUIUpdate(binding.UICachedValue); - break; + { + var val = binding.UICachedValue; + var state = binding.ValidateValue(val); + binding.SetValidationState(state); + binding.CommitToDataSource(val); + binding.NotifyUIUpdate(val, state); + break; + } } } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs new file mode 100644 index 0000000..10312fe --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs @@ -0,0 +1,79 @@ +using System; +using System.Reflection; + +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 子属性值访问器 — 通过父访问器 + PropertyPath 访问结构的子字段 + /// + /// 用于拆分属性([XuiavrgSplit])场景,如 Vector3 的 x/y/z 分量。 + /// GetValue 通过反射读取父值的子字段/属性;SetValue 由绑定层的 WriteAction 处理。 + /// + internal class AvrgSubPropertyAccessor : IAvrgValueAccessor + { + private readonly IAvrgValueAccessor _parentAccessor; + private readonly string _propertyPath; + private readonly AvrgMemberMetaData _metaData; + + public AvrgSubPropertyAccessor( + IAvrgValueAccessor parentAccessor, + string propertyPath, + AvrgMemberMetaData metaData) + { + _parentAccessor = parentAccessor ?? throw new ArgumentNullException(nameof(parentAccessor)); + _propertyPath = propertyPath ?? throw new ArgumentNullException(nameof(propertyPath)); + _metaData = metaData; + } + + public Type ValueType => _metaData?.ValueType ?? typeof(float); + + public bool IsReadOnly => _parentAccessor.IsReadOnly; + + public object GetValue() + { + var parentValue = _parentAccessor.GetValue(); + if (parentValue == null || string.IsNullOrEmpty(_propertyPath)) + return null; + + var type = parentValue.GetType(); + var field = type.GetRuntimeField(_propertyPath); + if (field != null) return field.GetValue(parentValue); + + var prop = type.GetRuntimeProperty(_propertyPath); + return prop?.GetValue(parentValue); + } + + /// + /// 子属性设置由绑定层的 WriteAction 统一处理(ReadSubProperty/WriteSubProperty), + /// 此方法不应被直接调用。 + /// + public void SetValue(object value) + { + var parentValue = _parentAccessor.GetValue(); + if (parentValue == null) return; + + var type = parentValue.GetType(); + var field = type.GetRuntimeField(_propertyPath); + if (field != null) + { + field.SetValue(parentValue, value); + _parentAccessor.SetValue(parentValue); + return; + } + + var prop = type.GetRuntimeProperty(_propertyPath); + if (prop != null && prop.CanWrite) + { + prop.SetValue(parentValue, value); + _parentAccessor.SetValue(parentValue); + } + } + + public string FormatDisplay(object value) + { + if (_metaData?.FormatText != null && value is IFormattable formattable) + return formattable.ToString(_metaData.FormatText, null); + return value?.ToString() ?? ""; + } + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs new file mode 100644 index 0000000..722bdd1 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs @@ -0,0 +1,13 @@ +namespace XericUI.ReflectionUIGenerator +{ + /// + /// 值校验状态枚举 + /// Normal = 正常(通过校验),Warning = 警告(值在警告范围但放行),Invalid = 无效(值被驳回不写入) + /// + public enum ValidationState + { + Normal = 0, + Warning = 1, + Invalid = 2 + } +} diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs index e658f02..6be003c 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiFieldTempEntry.cs @@ -308,14 +308,14 @@ namespace XericUI.ReflectionUIGenerator foreach (var binding in _bindings) { var value = binding.ReadFromDataSource(); - binding.NotifyUIUpdate(value); + binding.NotifyUIUpdate(value, ValidationState.Normal); if (binding.SubBindings?.Count > 0) { foreach (var subBinding in binding.SubBindings) { var subValue = subBinding.ReadFromDataSource(); - subBinding.NotifyUIUpdate(subValue); + subBinding.NotifyUIUpdate(subValue, ValidationState.Normal); } } } diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs index 6aae0e9..d2d81a2 100644 --- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/XuiavrgInspectorManager.cs @@ -127,16 +127,17 @@ namespace XericUI.ReflectionUIGenerator if (bindingCollection != null) { - var matchedBindings = bindingCollection.FindByMemberName(metaData.MemberName); - if (matchedBindings != null) + var scheduler = bindingCollection.Scheduler; + var matchedBinding = bindingCollection.FindByMemberName(metaData.MemberName); + if (matchedBinding != null) { - entry.AddBinding(matchedBindings, null); + entry.AddBinding(matchedBinding, scheduler); } else { foreach (var binding in bindingCollection) { - entry.AddBinding(binding, null); + entry.AddBinding(binding, scheduler); break; } } @@ -156,13 +157,14 @@ namespace XericUI.ReflectionUIGenerator if (CurrentEntries != null && bindingCollection != null) { + var scheduler = bindingCollection.Scheduler; foreach (var entry in CurrentEntries) { if (entry is XuiFieldTempEntry tempEntry) { var matched = bindingCollection.FindByMemberName(entry.EntryName); if (matched != null) - tempEntry.AddBinding(matched, null); + tempEntry.AddBinding(matched, scheduler); } } } @@ -457,5 +459,7 @@ namespace XericUI.ReflectionUIGenerator _leafGroupMap.Clear(); } + + #endregion } } \ No newline at end of file diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs index 9790e8a..d857e9c 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs @@ -4,20 +4,17 @@ using UnityEngine.UI; namespace XericUI.ReflectionUIGenerator { /// - /// 颜色值组件 — 处理 Color 类型的显示 - /// 通过 Image.color 展示当前颜色值 - /// 颜色修改可通过 RGB 子节点 InputField 或额外的颜色选择逻辑完成 + /// Color 值组件 — 通过 Image 显示颜色,可选点击弹出颜色选择器 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDColorValue")] public class XUIAvrgMDColorValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("用于展示颜色的 Image 组件(未设置则自动查找)")] - private Image _colorDisplay; + [SerializeField, Tooltip("关联的 Image 组件(未设置则自动查找)")] + private Image _colorImage; private void Awake() { - if (_colorDisplay == null) _colorDisplay = GetComponent(); + if (_colorImage == null) _colorImage = GetComponent(); } public override void BindToAccessor( @@ -30,29 +27,23 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null) return; - // 数据 → UI - binding.OnUIDataChanged += value => + binding.OnUIDataChanged += (value, state) => { - if (value is Color color && _colorDisplay != null) - { - _colorDisplay.color = color; - } + UpdateValueDisplay(value, state); }; + binding.OnValidationStateChanged += OnValidationStateChanged; - // 首次同步 var initialValue = accessor.GetValue(); - if (initialValue is Color initColor && _colorDisplay != null) - { - _colorDisplay.color = initColor; - } + UpdateValueDisplay(initialValue, ValidationState.Normal); } - protected override void UpdateDisplayValue(object value) + protected override void UpdateValueDisplay(object value, ValidationState state) { - if (value is Color color && _colorDisplay != null) - { - _colorDisplay.color = color; - } + // 值显示:刷新 Image.color + if (_colorImage != null && value is Color c) + _colorImage.color = c; + + base.UpdateValueDisplay(value, state); } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs index a62e344..20dbdb9 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; +using System; using System.Linq; using UnityEngine; -using UnityEngine.UI; namespace XericUI.ReflectionUIGenerator { @@ -20,6 +20,8 @@ namespace XericUI.ReflectionUIGenerator private Dropdown _dropdown; #endif + private List _options; + private void Awake() { #if dUI_TextMeshPro @@ -29,6 +31,24 @@ namespace XericUI.ReflectionUIGenerator #endif } + // ============ 对象池生命周期 ============ + + public override void OnPoolGet(AvrgMemberMetaData metaData) + { + base.OnPoolGet(metaData); + _options = BuildOptions(metaData?.ValueType); + } + + public override void OnPoolRelease() + { + if (_dropdown != null) + _dropdown.onValueChanged.RemoveAllListeners(); + _options = null; + base.OnPoolRelease(); + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -39,66 +59,71 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null || _dropdown == null) return; - // 构建选项列表 - var options = BuildOptions(accessor.ValueType); + // 填充选项 _dropdown.ClearOptions(); - _dropdown.AddOptions(options); + _dropdown.AddOptions(_options); // UI → 数据 _dropdown.onValueChanged.AddListener(index => { - if (index >= 0 && index < options.Count) + if (index >= 0 && index < _options.Count) { - object convertedVal = ConvertOptionToValue(options[index], accessor.ValueType); + object convertedVal = ConvertOptionToValue(_options[index], accessor.ValueType); binding.WriteFromUI(convertedVal); scheduler?.MarkUIDirty(binding); } }); - // 数据 → UI - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { - int idx = FindOptionIndex(value, options); + int idx = FindOptionIndex(value); if (idx >= 0) _dropdown.SetValueWithoutNotify(idx); + + base.UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); - int initialIdx = FindOptionIndex(initialValue, options); + int initialIdx = FindOptionIndex(initialValue); if (initialIdx >= 0) _dropdown.SetValueWithoutNotify(initialIdx); + UpdateValueDisplay(initialValue, ValidationState.Normal); } - private List BuildOptions(System.Type valueType) + private List BuildOptions(Type valueType) { if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) return BoundMetaData.PresetOptions.ToList(); if (valueType != null && valueType.IsEnum) - return System.Enum.GetNames(valueType).ToList(); + return Enum.GetNames(valueType).ToList(); return new List(); } - private int FindOptionIndex(object value, List options) + private int FindOptionIndex(object value) { - if (value == null || options.Count == 0) return -1; + if (value == null || _options == null || _options.Count == 0) return -1; string valueStr = value.ToString(); - for (int i = 0; i < options.Count; i++) + for (int i = 0; i < _options.Count; i++) { - if (options[i] == valueStr) return i; + if (_options[i] == valueStr) return i; } return -1; } - private static object ConvertOptionToValue(string option, System.Type targetType) + private static object ConvertOptionToValue(string option, Type targetType) { if (targetType == typeof(string)) return option; if (targetType == typeof(int) && int.TryParse(option, out int intVal)) return intVal; if (targetType != null && targetType.IsEnum) - return System.Enum.Parse(targetType, option); + return Enum.Parse(targetType, option); return option; } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs index 32a7e9d..5cf2ef4 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs @@ -1,12 +1,11 @@ using System; using UnityEngine; using UnityEngine.UI; -using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { /// - /// InputField 值组件 — 处理 string / float / int 类型的文本输入交互 + /// InputField 值组件 — 处理 string / float / int / double 类型的文本输入交互 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDInputFieldValue")] public class XUIAvrgMDInputFieldValue : XUIAvrgMDValue @@ -26,10 +25,7 @@ namespace XericUI.ReflectionUIGenerator [SerializeField, Tooltip("减少按钮(点击按步进值 -)")] private Button _decrementButton; - // ============ 样式表参数 ============ - private readonly StyleMember _styleStepValue = new() { StylePath = "xuiavrg/input/stepValue" }; - - // ============ 生命周期 ============ + private const float DefaultStep = 1f; private void Awake() { @@ -40,16 +36,18 @@ namespace XericUI.ReflectionUIGenerator #endif } - protected override void OnEnable() - { - base.OnEnable(); - _styleStepValue.Register(); - } + // ============ 对象池生命周期 ============ - protected override void OnDisable() + public override void OnPoolRelease() { - base.OnDisable(); - _styleStepValue.Unregister(); + if (_incrementButton != null) + _incrementButton.onClick.RemoveAllListeners(); + if (_decrementButton != null) + _decrementButton.onClick.RemoveAllListeners(); + if (_inputField != null) + _inputField.onEndEdit.RemoveAllListeners(); + + base.OnPoolRelease(); } // ============ 核心绑定 ============ @@ -64,6 +62,7 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null || _inputField == null) return; + // 根据类型设置输入模式 #if dUI_TextMeshPro if (accessor.ValueType == typeof(float) || accessor.ValueType == typeof(int) || accessor.ValueType == typeof(double)) @@ -92,33 +91,29 @@ namespace XericUI.ReflectionUIGenerator scheduler?.MarkUIDirty(binding); }); - // 数据 → UI - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { _inputField.SetTextWithoutNotify(accessor.FormatDisplay(value)); + base.UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); _inputField.SetTextWithoutNotify(accessor.FormatDisplay(initialValue)); - - RefreshCommonNodes(); + UpdateValueDisplay(initialValue, ValidationState.Normal); } // ============ 步进 ============ - private float GetStepValue() - { - var val = GetStyleValue(_styleStepValue); - return val?.GetValueAsFloat() ?? 1f; - } - private void OnIncrementClicked() { if (CurrentAccessor?.IsReadOnly == true || _inputField == null) return; if (!float.TryParse(_inputField.text, out float current)) return; - float step = GetStepValue(); - float newVal = current + step; + float newVal = current + DefaultStep; _inputField.text = CurrentAccessor.FormatDisplay( ConvertValueForAccessor(newVal, CurrentAccessor.ValueType)); } @@ -127,12 +122,32 @@ namespace XericUI.ReflectionUIGenerator { if (CurrentAccessor?.IsReadOnly == true || _inputField == null) return; if (!float.TryParse(_inputField.text, out float current)) return; - float step = GetStepValue(); - float newVal = current - step; + float newVal = current - DefaultStep; _inputField.text = CurrentAccessor.FormatDisplay( ConvertValueForAccessor(newVal, CurrentAccessor.ValueType)); } + // ============ 验证状态回调 ============ + + protected override void OnValidationStateChanged(ValidationState state) + { + base.OnValidationStateChanged(state); + + // InputField 文字颜色随验证状态变化(通过 Text 组件) + if (_inputField != null) + { + var textComponent = _inputField.textComponent; + if (textComponent != null) + { + textComponent.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) + : Color.white; + } + } + } + private static object ConvertValueForAccessor(float val, Type targetType) { if (targetType == typeof(int)) return (int)val; diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs index b161c89..85c63b7 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs @@ -1,32 +1,19 @@ using UnityEngine; +using UnityEngine.UI; namespace XericUI.ReflectionUIGenerator { /// - /// 标签值组件 — 处理只读文本的纯展示 - /// 适用于 Label / Context / Unit / ReadOnly 等展示型 Tag + /// Label 只读值组件 — 纯文本展示,无交互控件 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDLabelValue")] public class XUIAvrgMDLabelValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("关联的 Text 组件(未设置则自动查找)")] - private UnityEngine.UI.Text _uiText; - #if dUI_TextMeshPro - [SerializeField] - [Tooltip("关联的 TMP_Text 组件(未设置则自动查找)")] + [SerializeField, Tooltip("关联的 TMP_Text 组件(标签文本 + 值文本共用一个 Text 时使用)")] private TMPro.TMP_Text _tmpText; #endif - private void Awake() - { - if (_uiText == null) _uiText = GetComponent(); -#if dUI_TextMeshPro - if (_tmpText == null) _tmpText = GetComponent(); -#endif - } - public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -37,31 +24,14 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null) return; - // 数据 → UI - binding.OnUIDataChanged += value => + binding.OnUIDataChanged += (value, state) => { - SetText(value); + UpdateValueDisplay(value, state); }; + binding.OnValidationStateChanged += OnValidationStateChanged; - // 首次同步 var initialValue = accessor.GetValue(); - SetText(initialValue); - } - - private void SetText(object value) - { - string displayStr = value?.ToString() ?? ""; - -#if dUI_TextMeshPro - if (_tmpText != null) - { - _tmpText.text = displayStr; - return; - } -#endif - - if (_uiText != null) - _uiText.text = displayStr; + UpdateValueDisplay(initialValue, ValidationState.Normal); } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs index f5d2cfb..5f3f6d5 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs @@ -1,19 +1,17 @@ using System; using UnityEngine; using UnityEngine.UI; -using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { /// /// Slider 值组件 — 处理 float / int / double 类型的滑块交互 - /// 适用于 Shader Range 属性或标记了 [XuiavrgFieldTag(Slider)] 的字段 + /// 适用 Shader Range 属性或标记了 [XuiavrgFieldTag(Slider)] 的字段 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDSliderValue")] public class XUIAvrgMDSliderValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("关联的 Slider 组件(未设置则自动查找)")] + [SerializeField, Tooltip("关联的 Slider 组件(未设置则自动查找)")] private Slider _slider; [Header("Slider 专属插槽")] @@ -24,37 +22,44 @@ namespace XericUI.ReflectionUIGenerator private Button _decrementButton; #if dUI_TextMeshPro - [SerializeField] - [Tooltip("值显示标签(可选,未设置则不更新)")] + [SerializeField, Tooltip("值显示标签(可选,未设置则不更新)")] private TMPro.TMP_Text _valueLabel; #endif - // ============ 样式表参数 ============ - private readonly StyleMember _styleDefaultStep = new() { StylePath = "xuiavrg/slider/defaultStep" }; - private readonly StyleMember _styleMinStep = new() { StylePath = "xuiavrg/slider/minStep" }; - private readonly StyleMember _styleMaxStep = new() { StylePath = "xuiavrg/slider/maxStep" }; - - // ============ 生命周期 ============ + private const float DefaultStep = 0.1f; private void Awake() { if (_slider == null) _slider = GetComponent(); } - protected override void OnEnable() + // ============ 对象池生命周期 ============ + + public override void OnPoolGet(AvrgMemberMetaData metaData) { - base.OnEnable(); - _styleDefaultStep.Register(); - _styleMinStep.Register(); - _styleMaxStep.Register(); + base.OnPoolGet(metaData); + + if (_slider == null) return; + + // 从元数据初始化 Slider 参数 + if (metaData?.RangeMin.HasValue == true) + _slider.minValue = metaData.RangeMin.Value; + if (metaData?.RangeMax.HasValue == true) + _slider.maxValue = metaData.RangeMax.Value; + _slider.wholeNumbers = metaData?.ValueType == typeof(int); } - protected override void OnDisable() + public override void OnPoolRelease() { - base.OnDisable(); - _styleDefaultStep.Unregister(); - _styleMinStep.Unregister(); - _styleMaxStep.Unregister(); + // 清除按钮监听 + if (_incrementButton != null) + _incrementButton.onClick.RemoveAllListeners(); + if (_decrementButton != null) + _decrementButton.onClick.RemoveAllListeners(); + if (_slider != null) + _slider.onValueChanged.RemoveAllListeners(); + + base.OnPoolRelease(); } // ============ 核心绑定 ============ @@ -69,17 +74,6 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null || _slider == null) return; - // 设置 Slider 参数 - _slider.wholeNumbers = accessor.ValueType == typeof(int); - - if (BoundMetaData != null) - { - if (BoundMetaData.RangeMin.HasValue) - _slider.minValue = BoundMetaData.RangeMin.Value; - if (BoundMetaData.RangeMax.HasValue) - _slider.maxValue = BoundMetaData.RangeMax.Value; - } - // 增减按钮 if (_incrementButton != null) _incrementButton.onClick.AddListener(OnIncrementClicked); @@ -99,8 +93,8 @@ namespace XericUI.ReflectionUIGenerator #endif }); - // 数据 → UI - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { float sliderVal = Convert.ToSingle(value); _slider.SetValueWithoutNotify(sliderVal); @@ -109,8 +103,14 @@ namespace XericUI.ReflectionUIGenerator if (_valueLabel != null) _valueLabel.text = accessor.FormatDisplay(value); #endif + + // 委托基类处理标签合成 + 值文本 + base.UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); if (initialValue != null) @@ -121,23 +121,14 @@ namespace XericUI.ReflectionUIGenerator _valueLabel.text = accessor.FormatDisplay(initialValue); #endif } - - RefreshCommonNodes(); + UpdateValueDisplay(initialValue ?? 0f, ValidationState.Normal); } // ============ 步进值 ============ private float GetStepValue() { - var val = GetStyleValue(_styleDefaultStep); - if (val != null) - { - float step = val.GetValueAsFloat(); - float min = GetStyleValue(_styleMinStep)?.GetValueAsFloat() ?? 0.001f; - float max = GetStyleValue(_styleMaxStep)?.GetValueAsFloat() ?? 1000f; - return Mathf.Clamp(step, min, max); - } - return _slider.wholeNumbers ? 1f : 0.1f; + return _slider.wholeNumbers ? 1f : DefaultStep; } private void OnIncrementClicked() @@ -158,6 +149,28 @@ namespace XericUI.ReflectionUIGenerator _slider.value = newVal; } + // ============ 验证状态回调 ============ + + protected override void OnValidationStateChanged(ValidationState state) + { + base.OnValidationStateChanged(state); + + // Slider 填充色随验证状态变化 + if (_slider != null) + { + var fillArea = _slider.fillRect; + if (fillArea != null && fillArea.TryGetComponent(out var fill)) + { + fill.color = state switch + { + ValidationState.Invalid => Color.red, + ValidationState.Warning => new Color(1f, 0.92f, 0.016f), + _ => Color.white + }; + } + } + } + private static object ConvertValueForAccessor(float val, Type targetType) { if (targetType == typeof(int)) return (int)val; diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs index 35e109f..ed4c08e 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs @@ -4,25 +4,22 @@ using UnityEngine.UI; namespace XericUI.ReflectionUIGenerator { /// - /// 纹理值组件 — 处理 Texture 类型的预览显示 - /// 通过 RawImage.texture 展示当前纹理 + /// Texture 值组件 — 通过 RawImage 显示纹理 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDTextureValue")] public class XUIAvrgMDTextureValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("用于展示纹理的 RawImage 组件(未设置则自动查找)")] - private RawImage _textureDisplay; + [SerializeField, Tooltip("关联的 RawImage 组件(未设置则自动查找)")] + private RawImage _rawImage; #if dUI_TextMeshPro - [SerializeField] - [Tooltip("纹理名称标签(可选,用于显示纹理资源名)")] + [SerializeField, Tooltip("纹理名称标签(可选)")] private TMPro.TMP_Text _nameLabel; #endif private void Awake() { - if (_textureDisplay == null) _textureDisplay = GetComponent(); + if (_rawImage == null) _rawImage = GetComponent(); } public override void BindToAccessor( @@ -35,35 +32,29 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null) return; - // 数据 → UI - binding.OnUIDataChanged += value => + binding.OnUIDataChanged += (value, state) => { - if (value is Texture tex && _textureDisplay != null) - { - _textureDisplay.texture = tex; - } + UpdateValueDisplay(value, state); + }; + binding.OnValidationStateChanged += OnValidationStateChanged; + var initialValue = accessor.GetValue(); + UpdateValueDisplay(initialValue, ValidationState.Normal); + } + + protected override void UpdateValueDisplay(object value, ValidationState state) + { + // 值显示:刷新 RawImage.texture + if (_rawImage != null && value is Texture tex) + { + _rawImage.texture = tex; #if dUI_TextMeshPro if (_nameLabel != null) - { - _nameLabel.text = value is Texture t && t != null ? t.name : "None"; - } + _nameLabel.text = tex?.name ?? ""; #endif - }; - - // 首次同步 - var initialValue = accessor.GetValue(); - if (initialValue is Texture initTex && _textureDisplay != null) - { - _textureDisplay.texture = initTex; } -#if dUI_TextMeshPro - if (_nameLabel != null) - { - _nameLabel.text = initialValue is Texture t && t != null ? t.name : "None"; - } -#endif + base.UpdateValueDisplay(value, state); } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs index 01f00f8..9c4ff8a 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs @@ -56,8 +56,8 @@ namespace XericUI.ReflectionUIGenerator }); } - // 数据 → UI - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { string valueStr = value?.ToString(); for (int i = 0; i < _toggles.Count && i < _optionLabels.Count; i++) @@ -65,11 +65,16 @@ namespace XericUI.ReflectionUIGenerator if (_optionLabels[i] == valueStr) { _toggles[i].SetIsOnWithoutNotify(true); - return; + break; } } + + base.UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); string initStr = initialValue?.ToString(); diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs index 3d741cf..ee63b89 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs @@ -1,43 +1,29 @@ using UnityEngine; using UnityEngine.UI; -using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { /// /// Toggle 值组件 — 处理 bool 类型的开关交互 - /// 支持样式表控制开关颜色 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDToggleValue")] public class XUIAvrgMDToggleValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("关联的 Toggle 组件(未设置则自动查找)")] + [SerializeField, Tooltip("关联的 Toggle 组件(未设置则自动查找)")] private Toggle _toggle; - // ============ 样式表参数 ============ - private readonly StyleMember _styleOnColor = new() { StylePath = "xuiavrg/toggle/onColor" }; - private readonly StyleMember _styleOffColor = new() { StylePath = "xuiavrg/toggle/offColor" }; - - // ============ 生命周期 ============ - private void Awake() { if (_toggle == null) _toggle = GetComponent(); } - protected override void OnEnable() - { - base.OnEnable(); - _styleOnColor.Register(); - _styleOffColor.Register(); - } + // ============ 对象池生命周期 ============ - protected override void OnDisable() + public override void OnPoolRelease() { - base.OnDisable(); - _styleOnColor.Unregister(); - _styleOffColor.Unregister(); + if (_toggle != null) + _toggle.onValueChanged.RemoveAllListeners(); + base.OnPoolRelease(); } // ============ 核心绑定 ============ @@ -60,36 +46,53 @@ namespace XericUI.ReflectionUIGenerator { binding.WriteFromUI(val); scheduler?.MarkUIDirty(binding); - ApplyToggleColor(val); }); - // 数据 → UI - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { bool isOn = value is bool b && b; _toggle.SetIsOnWithoutNotify(isOn); - ApplyToggleColor(isOn); + ApplyToggleColor(isOn, state); + + base.UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); bool initialOn = initialValue is bool bv && bv; _toggle.SetIsOnWithoutNotify(initialOn); - ApplyToggleColor(initialOn); - - RefreshCommonNodes(); + ApplyToggleColor(initialOn, ValidationState.Normal); + UpdateValueDisplay(initialValue, ValidationState.Normal); } - // ============ 样式颜色 ============ + // ============ 验证状态回调 ============ - private void ApplyToggleColor(bool isOn) + protected override void OnValidationStateChanged(ValidationState state) { - var target = _toggle.targetGraphic; + base.OnValidationStateChanged(state); + + // Switch 验证状态也影响 targetGraphic 颜色 + if (_toggle != null) + { + ApplyToggleColor(_toggle.isOn, state); + } + } + + private void ApplyToggleColor(bool isOn, ValidationState state) + { + var target = _toggle?.targetGraphic; if (target == null) return; - var colorVal = isOn ? GetStyleValue(_styleOnColor) : GetStyleValue(_styleOffColor); - if (colorVal != null) - target.color = colorVal; + if (state == ValidationState.Invalid) + target.color = Color.red; + else if (state == ValidationState.Warning) + target.color = new Color(1f, 0.92f, 0.016f); + else + target.color = isOn ? Color.green : new Color(0.5f, 0.5f, 0.5f, 1f); } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs index 6948365..9ddee2a 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using System.Text; using UnityEngine; -using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -9,11 +9,11 @@ namespace XericUI.ReflectionUIGenerator /// /// 职责: /// 1. 提供 EntryTag/TagIndex 供 XuiFieldTempEntry 匹配 - /// 2. 持有绑定的元数据引用 - /// 3. 通过 virtual BindToAccessor() 让子类自行管理 UI 交互事件和显示更新 - /// 4. 初始化时广播元数据到所有子节点上的 IXUIAvrgMDValueStyle + /// 2. 持有绑定的元数据引用,管理验证状态 + /// 3. 通过 virtual BindToAccessor() 让子类自行管理 UI 交互事件 + /// 4. 对象池生命周期:OnPoolGet(meta) 初始化、OnPoolRelease() 重置 /// 5. 定义绑定槽(BindingSlots),供拆分属性子绑定匹配 - /// 6. 管理通用 UI 插槽(标签、警告、只读、单位、值显示)+ SuperStyleSheet 样式读取 + /// 6. 通过 UpdateValueDisplay() 合成标签文本(名称 + 只读 + 警告标记) /// [AddComponentMenu("Xeric Library/UI Action Vessel/XUIAvrgMDValue")] public class XUIAvrgMDValue : MonoBehaviour @@ -47,29 +47,29 @@ namespace XericUI.ReflectionUIGenerator [Tooltip("拆分属性子绑定槽列表 — 用于 XuiavrgSplit 的子属性匹配")] private List _bindingSlots = new List(); - // ============ 通用 UI 插槽 ============ + // ============ UI 文本插槽 ============ - [Header("通用 UI 插槽")] - [SerializeField, Tooltip("字段标签节点 — 显示字段名称 + 只读前缀。为 null 则不显示标签行")] - protected GameObject fieldLabelNode; + [Header("UI 文本插槽")] + [SerializeField, Tooltip("字段标签文本 — 显示 \"名称 (只读) ⚠警告\",为 null 则不显示标签")] +#if dUI_TextMeshPro + protected TMPro.TMP_Text _fieldLabelText; +#else + protected UnityEngine.UI.Text _fieldLabelText; +#endif - [SerializeField, Tooltip("警告样式节点 — 验证失败时 SetActive(true),颜色由样式表控制")] - protected GameObject warningNode; + [SerializeField, Tooltip("值显示文本 — 可为 null(Slider/Color/Texture 子类不依赖文本)")] +#if dUI_TextMeshPro + protected TMPro.TMP_Text _valueDisplayText; +#else + protected UnityEngine.UI.Text _valueDisplayText; +#endif - [SerializeField, Tooltip("只读标记节点 — IsReadOnly 时 SetActive(true)")] - protected GameObject readOnlyNode; - - [SerializeField, Tooltip("单位文本节点 — 显示元数据的 UnitLabelName")] - protected GameObject unitTextNode; - - [SerializeField, Tooltip("值显示文本节点 — 可为 null(子类如 Slider/Color 不依赖文本)")] - protected GameObject valueTextNode; - - // ============ 样式表参数(路径硬编码,命名空间从 BoundMetaData.StyleNamespace 获取)============ - - private readonly StyleMember _styleStepValue = new() { StylePath = "xuiavrg/base/stepValue" }; - private readonly StyleMember _styleReadOnlyPrefix = new() { StylePath = "xuiavrg/base/readOnlyPrefix" }; - private readonly StyleMember _styleWarningColor = new() { StylePath = "xuiavrg/base/warningColor" }; + [SerializeField, Tooltip("单位文本 — 可为 null")] +#if dUI_TextMeshPro + protected TMPro.TMP_Text _unitText; +#else + protected UnityEngine.UI.Text _unitText; +#endif // ============ 公共属性 ============ @@ -82,26 +82,44 @@ namespace XericUI.ReflectionUIGenerator /// 当前关联的 MVVM 绑定 public AvrgMvvmBinding CurrentBinding { get; protected set; } + /// 当前验证状态 + public ValidationState CurrentValidationState { get; protected set; } = ValidationState.Normal; + public XuiavrgFieldTag EntryTag => _entryTag; public int TagIndex => _tagIndex; /// 只读访问绑定槽列表 public IReadOnlyList AvailableBindingSlots => _bindingSlots; - // ============ 生命周期 ============ + // ============ 对象池生命周期 ============ - protected virtual void OnEnable() + /// + /// 从对象池取出时调用 — 注入元数据并初始化子属性 + /// 子类可覆写以初始化专属组件参数(Slider.minValue/maxValue 等) + /// + public virtual void OnPoolGet(AvrgMemberMetaData metaData) { - _styleStepValue.Register(); - _styleReadOnlyPrefix.Register(); - _styleWarningColor.Register(); + BoundMetaData = metaData; + CurrentValidationState = ValidationState.Normal; + + // 子类覆写点 } - protected virtual void OnDisable() + /// + /// 回收到对象池时调用 — 重置所有状态到初始值 + /// 子类可覆写以清理专属组件事件 + /// + public virtual void OnPoolRelease() { - _styleStepValue.Unregister(); - _styleReadOnlyPrefix.Unregister(); - _styleWarningColor.Unregister(); + CurrentAccessor = null; + CurrentBinding = null; + BoundMetaData = null; + CurrentValidationState = ValidationState.Normal; + + // 重置 UI 文本 + if (_fieldLabelText != null) _fieldLabelText.text = ""; + if (_valueDisplayText != null) _valueDisplayText.text = ""; + if (_unitText != null) _unitText.text = ""; } // ============ 核心绑定接口 ============ @@ -119,44 +137,107 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null) return; - // 默认:数据 → UI 单向同步 - binding.OnUIDataChanged += value => + // 数据 → UI(带验证状态) + binding.OnUIDataChanged += (value, state) => { - UpdateDisplayValue(value); + UpdateValueDisplay(value, state); }; + // 验证状态变更 + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); - UpdateDisplayValue(initialValue); + UpdateValueDisplay(initialValue, ValidationState.Normal); + } - // 刷新通用插槽 - RefreshCommonNodes(); + // ============ UI 更新 ============ + + /// + /// 根据值和验证状态更新 UI 显示 + /// 子类可覆写以处理专属组件更新(Slider.value, Image.color 等) + /// 基类负责合成标签文本、值文本颜色、单位文本 + /// + protected virtual void UpdateValueDisplay(object value, ValidationState state) + { + // 1. 字段标签合成 + if (_fieldLabelText != null) + { + string label = BuildLabel(state); + _fieldLabelText.text = label; + } + + // 2. 值显示 + if (_valueDisplayText != null) + { + string display = CurrentAccessor?.FormatDisplay(value) ?? value?.ToString() ?? ""; + _valueDisplayText.text = display; + + _valueDisplayText.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) // yellow + : Color.white; + } + + // 3. 单位文本 + if (_unitText != null) + _unitText.text = BoundMetaData?.UnitLabelName ?? ""; } /// - /// 更新 UI 显示值(基类默认仅查找 Text/TMP_Text 设置文本) - /// 子类可覆写以实现专属更新逻辑(如 Slider.value, Image.color 等) + /// 合成标签文本:名称 + (只读) + 警告标记 /// - protected virtual void UpdateDisplayValue(object value) + protected string BuildLabel(ValidationState state) { - if (value == null) return; + var meta = BoundMetaData; + var sb = new StringBuilder(); + sb.Append(meta?.LabelName ?? meta?.MemberName ?? ""); -#if dUI_TextMeshPro - var tmpText = GetComponent(); - if (tmpText != null) - { - tmpText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); - return; - } -#endif + // 必填标记 + if (meta != null && meta.Required) + sb.Append(" *"); - var uiText = GetComponent(); - if (uiText != null) + // 只读标记 + if (CurrentAccessor?.IsReadOnly == true) + sb.Append(" (只读)"); + + // 警告/无效标记(用富文本颜色) + if (state == ValidationState.Warning) + sb.Append(" ⚠警告"); + else if (state == ValidationState.Invalid) + sb.Append(" ⚠无效值"); + + return sb.ToString(); + } + + // ============ 验证状态回调 ============ + + /// + /// 验证状态变更回调 — 子类可覆写以实现专属样式切换 + /// 如 Slider 填充色变黄/红、InputField 文字变色等 + /// + protected virtual void OnValidationStateChanged(ValidationState state) + { + CurrentValidationState = state; + + // 刷新标签文本(警告/无效标记在标签上) + if (_fieldLabelText != null) + _fieldLabelText.text = BuildLabel(state); + + // 值文本颜色 + if (_valueDisplayText != null) { - uiText.text = CurrentAccessor?.FormatDisplay(value) ?? value.ToString(); + _valueDisplayText.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) + : Color.white; } } + // ============ 元数据广播 ============ + /// /// 由绑定系统调用,传递元数据并广播到 IXUIAvrgMDValueStyle 子组件 /// @@ -166,7 +247,6 @@ namespace XericUI.ReflectionUIGenerator if (metaData == null) return; - // 广播元数据到子节点样式组件 var styles = GetComponentsInChildren(true); foreach (var style in styles) { @@ -176,60 +256,5 @@ namespace XericUI.ReflectionUIGenerator style.ApplyMetaData(metaData); } } - - // ============ 样式读取 ============ - - /// - /// 从样式表获取值(自动使用 BoundMetaData.StyleNamespace) - /// - protected StyleValue GetStyleValue(StyleMember member) - { - if (member == null) return null; - string ns = BoundMetaData?.StyleNamespace ?? "default"; - return StyleManager.GetValue(ns, member.StylePath); - } - - // ============ 通用 UI 插槽刷新 ============ - - /// 刷新所有通用 UI 插槽(标签、单位、只读、警告标记) - protected void RefreshCommonNodes() - { - var meta = BoundMetaData; - if (meta == null) return; - - // 1. 字段标签节点 - if (fieldLabelNode != null) - { - string prefix = ""; - if (CurrentAccessor?.IsReadOnly == true) - { - var pv = GetStyleValue(_styleReadOnlyPrefix); - prefix = pv?.GetValueAsString() ?? "[只读] "; - } - SetNodeText(fieldLabelNode, prefix + (meta.LabelName ?? meta.MemberName)); - } - - // 2. 单位文本节点 - if (unitTextNode != null) - SetNodeText(unitTextNode, meta.UnitLabelName ?? ""); - - // 3. 只读标记节点 - if (readOnlyNode != null) - readOnlyNode.SetActive(CurrentAccessor?.IsReadOnly == true); - - // 4. warningNode 由外部验证系统触发,此方法不直接控制 - } - - /// 在节点及其子节点上查找 Text / TMP_Text 并设置文本 - protected static void SetNodeText(GameObject node, string text) - { - if (node == null) return; -#if dUI_TextMeshPro - var t = node.GetComponentInChildren(true); - if (t != null) { t.text = text; return; } -#endif - var ut = node.GetComponentInChildren(true); - if (ut != null) ut.text = text; - } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs index 470eb39..bf9188f 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs @@ -1,6 +1,5 @@ using UnityEngine; using UnityEngine.UI; -using XericLibrary.Runtime.SuperStyleSheet; namespace XericUI.ReflectionUIGenerator { @@ -11,37 +10,39 @@ namespace XericUI.ReflectionUIGenerator [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDVectorValue")] public class XUIAvrgMDVectorValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("X 分量 InputField(可选,也可通过 BindingSlots 的子节点绑定)")] + [SerializeField, Tooltip("X 分量 InputField(可选,也可通过 BindingSlots 的子节点绑定)")] private InputField _inputX; - [SerializeField] - [Tooltip("Y 分量 InputField")] + [SerializeField, Tooltip("Y 分量 InputField")] private InputField _inputY; - [SerializeField] - [Tooltip("Z 分量 InputField(Vector3/Vector4 时使用)")] + [SerializeField, Tooltip("Z 分量 InputField(Vector3/Vector4 时使用)")] private InputField _inputZ; - [SerializeField] - [Tooltip("W 分量 InputField(Vector4 时使用)")] + [SerializeField, Tooltip("W 分量 InputField(Vector4 时使用)")] private InputField _inputW; - // ============ 样式表参数 ============ - private readonly StyleMember _styleLayoutDirection = new() { StylePath = "xuiavrg/vector/layoutDirection" }; + // ============ 对象池生命周期 ============ - // ============ 生命周期 ============ - - protected override void OnEnable() + public override void OnPoolGet(AvrgMemberMetaData metaData) { - base.OnEnable(); - _styleLayoutDirection.Register(); + base.OnPoolGet(metaData); + + int componentCount = GetComponentCount(metaData?.ValueType); + SetInputActive(_inputX, 0, componentCount); + SetInputActive(_inputY, 1, componentCount); + SetInputActive(_inputZ, 2, componentCount); + SetInputActive(_inputW, 3, componentCount); } - protected override void OnDisable() + public override void OnPoolRelease() { - base.OnDisable(); - _styleLayoutDirection.Unregister(); + if (_inputX != null) _inputX.onEndEdit.RemoveAllListeners(); + if (_inputY != null) _inputY.onEndEdit.RemoveAllListeners(); + if (_inputZ != null) _inputZ.onEndEdit.RemoveAllListeners(); + if (_inputW != null) _inputW.onEndEdit.RemoveAllListeners(); + + base.OnPoolRelease(); } // ============ 核心绑定 ============ @@ -56,18 +57,6 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null) return; - // 根据向量类型启用/禁用对应分量 - int componentCount = GetComponentCount(accessor.ValueType); - SetInputActive(_inputX, 0, componentCount); - SetInputActive(_inputY, 1, componentCount); - SetInputActive(_inputZ, 2, componentCount); - SetInputActive(_inputW, 3, componentCount); - - // 样式表控制排列方向 - var dirVal = GetStyleValue(_styleLayoutDirection); - string dir = dirVal?.GetValueAsString() ?? "Horizontal"; - ApplyLayoutDirection(dir); - // 为每个输入注册事件 BindInputField(_inputX, 0, accessor, binding, scheduler); BindInputField(_inputY, 1, accessor, binding, scheduler); @@ -75,41 +64,18 @@ namespace XericUI.ReflectionUIGenerator BindInputField(_inputW, 3, accessor, binding, scheduler); // 数据 → UI - binding.OnUIDataChanged += value => + binding.OnUIDataChanged += (value, state) => { UpdateAllInputs(value); + base.UpdateValueDisplay(value, state); }; + binding.OnValidationStateChanged += OnValidationStateChanged; + // 首次同步 var initialValue = accessor.GetValue(); UpdateAllInputs(initialValue); - - RefreshCommonNodes(); - } - - // ============ 布局 ============ - - private void ApplyLayoutDirection(string dir) - { - var layout = GetComponent(); - if (layout == null) return; - - if (dir == "Vertical") - { - if (layout is HorizontalLayoutGroup) - { - Destroy(layout); - gameObject.AddComponent(); - } - } - else - { - if (layout is VerticalLayoutGroup) - { - Destroy(layout); - gameObject.AddComponent(); - } - } + UpdateValueDisplay(initialValue, ValidationState.Normal); } // ============ 输入绑定 ============ From fe5ae4726cf4a8d78b0dc0db6ba50d63dfb9c232 Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sat, 25 Jul 2026 23:56:29 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=94=B6=E7=B4=A7=E5=90=84=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scripts/AvrgBindingTrie.cs.meta | 11 ++ .../Scripts/AvrgSubPropertyAccessor.cs.meta | 11 ++ .../Scripts/ValidationState.cs.meta | 11 ++ .../AvrgValues/XUIAvrgMDDropdownValue.cs | 49 ++++++- .../AvrgValues/XUIAvrgMDLabelValue.cs | 44 +++++- .../AvrgValues/XUIAvrgMDSliderValue.cs | 90 +++++++++--- .../AvrgValues/XUIAvrgMDToggleGroupValue.cs | 136 ++++++++++++++---- .../OverrideUI/AvrgValues/XUIAvrgMDValue.cs | 85 ++++++----- 8 files changed, 342 insertions(+), 95 deletions(-) create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs.meta create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs.meta diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs.meta new file mode 100644 index 0000000..fe68f26 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgBindingTrie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fbb9c5ea2b8b5644a49559e7b6260e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs.meta new file mode 100644 index 0000000..70496d4 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgSubPropertyAccessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0ab49f7e492a8d448cdcc9f214325f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs.meta b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs.meta new file mode 100644 index 0000000..eb94e88 --- /dev/null +++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/ValidationState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e20b793a32d670a4d95e20189dd27694 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs index 20dbdb9..05344ef 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs @@ -8,18 +8,24 @@ namespace XericUI.ReflectionUIGenerator /// /// Dropdown 值组件 — 处理 string / int / enum 类型的下拉选择交互 /// 预设选项来源:元数据的 PresetOptions 或 enum 类型的枚举名 + /// + /// 选项动态创建:OnPoolGet 时根据元数据 PresetOptions 构建选项列表, + /// BindToAccessor 时通过 AddOptions() 填充到 Dropdown 组件。 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDDropdownValue")] public class XUIAvrgMDDropdownValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("关联的 Dropdown 组件(未设置则自动查找)")] + [SerializeField, Tooltip("关联的 Dropdown 组件(未设置则自动查找)")] #if dUI_TextMeshPro private TMPro.TMP_Dropdown _dropdown; #else private Dropdown _dropdown; #endif + [Header("选项动态创建")] + [SerializeField, Tooltip("选项项预制体 Transform(可选,用于自定义选项创建逻辑)")] + private Transform _optionItemPrefab; + private List _options; private void Awake() @@ -37,16 +43,55 @@ namespace XericUI.ReflectionUIGenerator { base.OnPoolGet(metaData); _options = BuildOptions(metaData?.ValueType); + + // 动态创建选项对象(子类可覆写) + CreateOptionItems(); } public override void OnPoolRelease() { if (_dropdown != null) _dropdown.onValueChanged.RemoveAllListeners(); + + DestroyOptionItems(); _options = null; base.OnPoolRelease(); } + // ============ 动态选项创建 ============ + + /// + /// 根据预设选项动态创建选项子对象 + /// 子类可覆写以实现自定义选项创建(如图标、颜色等) + /// + protected virtual void CreateOptionItems() + { + if (_optionItemPrefab == null || _options == null) return; + + for (int i = 0; i < _options.Count; i++) + { + var item = Instantiate(_optionItemPrefab, _optionItemPrefab.parent); + item.name = $"Option_{i}_{_options[i]}"; + item.gameObject.SetActive(true); + } + } + + /// + /// 销毁动态创建的选项子对象 + /// + protected virtual void DestroyOptionItems() + { + if (_optionItemPrefab == null) return; + + var parent = _optionItemPrefab.parent; + for (int i = parent.childCount - 1; i >= 0; i--) + { + var child = parent.GetChild(i); + if (child != _optionItemPrefab) + Destroy(child.gameObject); + } + } + // ============ 核心绑定 ============ public override void BindToAccessor( diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs index 85c63b7..46e3a9d 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs @@ -1,19 +1,27 @@ using UnityEngine; -using UnityEngine.UI; namespace XericUI.ReflectionUIGenerator { /// /// Label 只读值组件 — 纯文本展示,无交互控件 + /// 拥有自己的 _valueDisplayText 用于显示值 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDLabelValue")] public class XUIAvrgMDLabelValue : XUIAvrgMDValue { + [SerializeField, Tooltip("值显示文本 — 显示当前字段的值")] #if dUI_TextMeshPro - [SerializeField, Tooltip("关联的 TMP_Text 组件(标签文本 + 值文本共用一个 Text 时使用)")] - private TMPro.TMP_Text _tmpText; + private TMPro.TMP_Text _valueDisplayText; +#else + private UnityEngine.UI.Text _valueDisplayText; #endif + public override void OnPoolRelease() + { + if (_valueDisplayText != null) _valueDisplayText.text = ""; + base.OnPoolRelease(); + } + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -33,5 +41,35 @@ namespace XericUI.ReflectionUIGenerator var initialValue = accessor.GetValue(); UpdateValueDisplay(initialValue, ValidationState.Normal); } + + protected override void UpdateValueDisplay(object value, ValidationState state) + { + if (_valueDisplayText != null) + { + _valueDisplayText.text = CurrentAccessor?.FormatDisplay(value) ?? value?.ToString() ?? ""; + + _valueDisplayText.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) + : Color.white; + } + + base.UpdateValueDisplay(value, state); + } + + protected override void OnValidationStateChanged(ValidationState state) + { + base.OnValidationStateChanged(state); + + if (_valueDisplayText != null) + { + _valueDisplayText.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) + : Color.white; + } + } } } diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs index 5f3f6d5..ab48756 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs @@ -15,17 +15,33 @@ namespace XericUI.ReflectionUIGenerator private Slider _slider; [Header("Slider 专属插槽")] + [SerializeField, Tooltip("值显示文本 — 显示当前 Slider 数值")] +#if dUI_TextMeshPro + private TMPro.TMP_Text _valueDisplayText; +#else + private UnityEngine.UI.Text _valueDisplayText; +#endif + + [SerializeField, Tooltip("最小值文本 — 显示 Slider 下限")] +#if dUI_TextMeshPro + private TMPro.TMP_Text _minValueText; +#else + private UnityEngine.UI.Text _minValueText; +#endif + + [SerializeField, Tooltip("最大值文本 — 显示 Slider 上限")] +#if dUI_TextMeshPro + private TMPro.TMP_Text _maxValueText; +#else + private UnityEngine.UI.Text _maxValueText; +#endif + [SerializeField, Tooltip("增加按钮(点击按步进值 +)")] private Button _incrementButton; [SerializeField, Tooltip("减少按钮(点击按步进值 -)")] private Button _decrementButton; -#if dUI_TextMeshPro - [SerializeField, Tooltip("值显示标签(可选,未设置则不更新)")] - private TMPro.TMP_Text _valueLabel; -#endif - private const float DefaultStep = 0.1f; private void Awake() @@ -51,7 +67,6 @@ namespace XericUI.ReflectionUIGenerator public override void OnPoolRelease() { - // 清除按钮监听 if (_incrementButton != null) _incrementButton.onClick.RemoveAllListeners(); if (_decrementButton != null) @@ -59,6 +74,11 @@ namespace XericUI.ReflectionUIGenerator if (_slider != null) _slider.onValueChanged.RemoveAllListeners(); + // 重置子类文本 + if (_valueDisplayText != null) _valueDisplayText.text = ""; + if (_minValueText != null) _minValueText.text = ""; + if (_maxValueText != null) _maxValueText.text = ""; + base.OnPoolRelease(); } @@ -74,6 +94,9 @@ namespace XericUI.ReflectionUIGenerator if (binding == null || accessor == null || _slider == null) return; + // 初始化上下限文本 + ApplyRangeTexts(); + // 增减按钮 if (_incrementButton != null) _incrementButton.onClick.AddListener(OnIncrementClicked); @@ -87,10 +110,7 @@ namespace XericUI.ReflectionUIGenerator binding.WriteFromUI(convertedVal); scheduler?.MarkUIDirty(binding); -#if dUI_TextMeshPro - if (_valueLabel != null) - _valueLabel.text = accessor.FormatDisplay(convertedVal); -#endif + RefreshValueText(convertedVal); }); // 数据 → UI(带验证状态) @@ -99,12 +119,9 @@ namespace XericUI.ReflectionUIGenerator float sliderVal = Convert.ToSingle(value); _slider.SetValueWithoutNotify(sliderVal); -#if dUI_TextMeshPro - if (_valueLabel != null) - _valueLabel.text = accessor.FormatDisplay(value); -#endif + RefreshValueText(value); + ApplyValueTextColor(state); - // 委托基类处理标签合成 + 值文本 base.UpdateValueDisplay(value, state); }; @@ -114,16 +131,45 @@ namespace XericUI.ReflectionUIGenerator // 首次同步 var initialValue = accessor.GetValue(); if (initialValue != null) - { _slider.SetValueWithoutNotify(Convert.ToSingle(initialValue)); -#if dUI_TextMeshPro - if (_valueLabel != null) - _valueLabel.text = accessor.FormatDisplay(initialValue); -#endif - } + + RefreshValueText(initialValue); UpdateValueDisplay(initialValue ?? 0f, ValidationState.Normal); } + // ============ 值显示 ============ + + protected override void UpdateValueDisplay(object value, ValidationState state) + { + ApplyValueTextColor(state); + base.UpdateValueDisplay(value, state); + } + + private void RefreshValueText(object value) + { + if (_valueDisplayText == null || CurrentAccessor == null) return; + _valueDisplayText.text = CurrentAccessor.FormatDisplay(value); + } + + private void ApplyValueTextColor(ValidationState state) + { + if (_valueDisplayText == null) return; + + _valueDisplayText.color = state == ValidationState.Invalid + ? Color.red + : state == ValidationState.Warning + ? new Color(1f, 0.92f, 0.016f) + : Color.white; + } + + private void ApplyRangeTexts() + { + if (_minValueText != null) + _minValueText.text = CurrentAccessor?.FormatDisplay(_slider.minValue) ?? _slider.minValue.ToString(); + if (_maxValueText != null) + _maxValueText.text = CurrentAccessor?.FormatDisplay(_slider.maxValue) ?? _slider.maxValue.ToString(); + } + // ============ 步进值 ============ private float GetStepValue() @@ -169,6 +215,8 @@ namespace XericUI.ReflectionUIGenerator }; } } + + ApplyValueTextColor(state); } private static object ConvertValueForAccessor(float val, Type targetType) diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs index 9c4ff8a..e19078e 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs @@ -8,26 +8,125 @@ namespace XericUI.ReflectionUIGenerator /// /// ToggleGroup 值组件 — 处理 string / int / enum 类型的单选组交互 /// 适用于标记了 [XuiavrgFieldToggleGroupValue] 的字段 + /// + /// 选项动态创建:OnPoolGet 时根据元数据 PresetOptions 数量, + /// 从 _toggleItemPrefab 实例化 Toggle 子对象。 /// [AddComponentMenu("Xeric Library/UI Action Vessel/Values/XUIAvrgMDToggleGroupValue")] public class XUIAvrgMDToggleGroupValue : XUIAvrgMDValue { - [SerializeField] - [Tooltip("关联的 ToggleGroup 组件(未设置则自动查找)")] + [SerializeField, Tooltip("关联的 ToggleGroup 组件(未设置则自动查找)")] private ToggleGroup _toggleGroup; + [Header("选项动态创建")] + [SerializeField, Tooltip("Toggle 项预制体 — 在 OnPoolGet 时按预设选项数量实例化")] + private Transform _toggleItemPrefab; + + [SerializeField, Tooltip("选项容器 Transform — 动态创建的 Toggle 挂载到此节点下")] + private Transform _optionsContainer; + private List _toggles = new List(); private List _optionLabels = new List(); private void Awake() { if (_toggleGroup == null) _toggleGroup = GetComponent(); - if (_toggleGroup != null) + } + + // ============ 对象池生命周期 ============ + + public override void OnPoolGet(AvrgMemberMetaData metaData) + { + base.OnPoolGet(metaData); + + // 构建选项列表 + BuildOptions(metaData?.ValueType); + + // 动态创建 Toggle 项 + CreateToggleItems(); + } + + public override void OnPoolRelease() + { + // 清理动态创建的 Toggle + foreach (var toggle in _toggles) { - _toggles = _toggleGroup.GetComponentsInChildren(true).ToList(); + if (toggle != null) + toggle.onValueChanged.RemoveAllListeners(); + } + DestroyToggleItems(); + + _toggles.Clear(); + _optionLabels.Clear(); + + base.OnPoolRelease(); + } + + // ============ 动态 Toggle 创建 ============ + + /// + /// 根据 _optionLabels 数量,从 _toggleItemPrefab 实例化 Toggle 子对象 + /// + private void CreateToggleItems() + { + var parent = _optionsContainer != null ? _optionsContainer : transform; + if (_toggleItemPrefab == null || _optionLabels.Count == 0) return; + + for (int i = 0; i < _optionLabels.Count; i++) + { + var item = Instantiate(_toggleItemPrefab, parent); + item.name = $"Toggle_{i}_{_optionLabels[i]}"; + item.gameObject.SetActive(true); + + var toggle = item.GetComponent(); + if (toggle != null) + { + toggle.group = _toggleGroup; + _toggles.Add(toggle); + + // 设置标签文本 + var label = item.GetComponentInChildren(true); + if (label != null) + label.text = _optionLabels[i]; + } } } + /// + /// 销毁动态创建的所有 Toggle 子对象 + /// + private void DestroyToggleItems() + { + var parent = _optionsContainer != null ? _optionsContainer : transform; + + foreach (var toggle in _toggles) + { + if (toggle != null) + Destroy(toggle.gameObject); + } + } + + private void BuildOptions(System.Type valueType) + { + _optionLabels.Clear(); + + // 1. 从元数据 PresetOptions + if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) + { + _optionLabels.AddRange(BoundMetaData.PresetOptions); + return; + } + + // 2. enum 类型 + if (valueType != null && valueType.IsEnum) + { + _optionLabels.AddRange(System.Enum.GetNames(valueType)); + return; + } + } + + // ============ 核心绑定 ============ + public override void BindToAccessor( IAvrgValueAccessor accessor, AvrgMvvmBinding binding, @@ -36,10 +135,14 @@ namespace XericUI.ReflectionUIGenerator CurrentAccessor = accessor; CurrentBinding = binding; - if (binding == null || accessor == null || _toggleGroup == null) return; + if (binding == null || accessor == null || _toggleGroup == null || _toggles.Count == 0) return; - // 构建选项 - BuildOptions(accessor.ValueType); + // 只读模式下禁用所有 Toggle + if (accessor.IsReadOnly) + { + foreach (var t in _toggles) + t.interactable = false; + } // 为每个 Toggle 注册事件 for (int i = 0; i < _toggles.Count && i < _optionLabels.Count; i++) @@ -88,25 +191,6 @@ namespace XericUI.ReflectionUIGenerator } } - private void BuildOptions(System.Type valueType) - { - _optionLabels.Clear(); - - // 1. 从元数据 PresetOptions - if (BoundMetaData?.PresetOptions != null && BoundMetaData.PresetOptions.Count > 0) - { - _optionLabels.AddRange(BoundMetaData.PresetOptions); - return; - } - - // 2. enum 类型 - if (valueType != null && valueType.IsEnum) - { - _optionLabels.AddRange(System.Enum.GetNames(valueType)); - return; - } - } - private static object ConvertOptionToValue(string option, System.Type targetType) { if (targetType == typeof(string)) return option; diff --git a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs index 9ddee2a..f8cc5c9 100644 --- a/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs +++ b/Runtime/OverrideUI/AvrgValues/XUIAvrgMDValue.cs @@ -13,7 +13,8 @@ namespace XericUI.ReflectionUIGenerator /// 3. 通过 virtual BindToAccessor() 让子类自行管理 UI 交互事件 /// 4. 对象池生命周期:OnPoolGet(meta) 初始化、OnPoolRelease() 重置 /// 5. 定义绑定槽(BindingSlots),供拆分属性子绑定匹配 - /// 6. 通过 UpdateValueDisplay() 合成标签文本(名称 + 只读 + 警告标记) + /// 6. 基类统一管理标签文本(名称 + 只读 + 警告标记)和单位可见性 + /// 值显示由各子类自行实现 /// [AddComponentMenu("Xeric Library/UI Action Vessel/XUIAvrgMDValue")] public class XUIAvrgMDValue : MonoBehaviour @@ -49,7 +50,7 @@ namespace XericUI.ReflectionUIGenerator // ============ UI 文本插槽 ============ - [Header("UI 文本插槽")] + [Header("UI 基类插槽")] [SerializeField, Tooltip("字段标签文本 — 显示 \"名称 (只读) ⚠警告\",为 null 则不显示标签")] #if dUI_TextMeshPro protected TMPro.TMP_Text _fieldLabelText; @@ -57,13 +58,6 @@ namespace XericUI.ReflectionUIGenerator protected UnityEngine.UI.Text _fieldLabelText; #endif - [SerializeField, Tooltip("值显示文本 — 可为 null(Slider/Color/Texture 子类不依赖文本)")] -#if dUI_TextMeshPro - protected TMPro.TMP_Text _valueDisplayText; -#else - protected UnityEngine.UI.Text _valueDisplayText; -#endif - [SerializeField, Tooltip("单位文本 — 可为 null")] #if dUI_TextMeshPro protected TMPro.TMP_Text _unitText; @@ -71,6 +65,9 @@ namespace XericUI.ReflectionUIGenerator protected UnityEngine.UI.Text _unitText; #endif + [SerializeField, Tooltip("单位容器 Transform — 单位文本为空时 SetActive(false) 隐藏")] + protected Transform _unitTransform; + // ============ 公共属性 ============ /// 绑定的元数据引用 @@ -102,7 +99,8 @@ namespace XericUI.ReflectionUIGenerator BoundMetaData = metaData; CurrentValidationState = ValidationState.Normal; - // 子类覆写点 + // 单位可见性:无单位文本则隐藏单位节点 + ApplyUnitVisibility(); } /// @@ -118,8 +116,10 @@ namespace XericUI.ReflectionUIGenerator // 重置 UI 文本 if (_fieldLabelText != null) _fieldLabelText.text = ""; - if (_valueDisplayText != null) _valueDisplayText.text = ""; if (_unitText != null) _unitText.text = ""; + + // 重置单位可见性 + if (_unitTransform != null) _unitTransform.gameObject.SetActive(true); } // ============ 核心绑定接口 ============ @@ -155,34 +155,17 @@ namespace XericUI.ReflectionUIGenerator /// /// 根据值和验证状态更新 UI 显示 - /// 子类可覆写以处理专属组件更新(Slider.value, Image.color 等) - /// 基类负责合成标签文本、值文本颜色、单位文本 + /// 子类必须覆写以处理专属组件更新(Slider.value, Image.color 等), + /// 并在覆写中调用 base.UpdateValueDisplay(value, state) 以刷新标签。 /// protected virtual void UpdateValueDisplay(object value, ValidationState state) { // 1. 字段标签合成 if (_fieldLabelText != null) - { - string label = BuildLabel(state); - _fieldLabelText.text = label; - } + _fieldLabelText.text = BuildLabel(state); - // 2. 值显示 - if (_valueDisplayText != null) - { - string display = CurrentAccessor?.FormatDisplay(value) ?? value?.ToString() ?? ""; - _valueDisplayText.text = display; - - _valueDisplayText.color = state == ValidationState.Invalid - ? Color.red - : state == ValidationState.Warning - ? new Color(1f, 0.92f, 0.016f) // yellow - : Color.white; - } - - // 3. 单位文本 - if (_unitText != null) - _unitText.text = BoundMetaData?.UnitLabelName ?? ""; + // 2. 单位文本 + 可见性 + ApplyUnitText(); } /// @@ -211,6 +194,32 @@ namespace XericUI.ReflectionUIGenerator return sb.ToString(); } + /// + /// 更新单位文本并控制可见性 + /// + protected void ApplyUnitText() + { + string unit = BoundMetaData?.UnitLabelName; + bool hasUnit = !string.IsNullOrEmpty(unit); + + if (_unitText != null) + _unitText.text = hasUnit ? unit : ""; + + if (_unitTransform != null) + _unitTransform.gameObject.SetActive(hasUnit); + } + + /// + /// 仅刷新单位可见性(不更新文本内容) + /// + protected void ApplyUnitVisibility() + { + if (_unitTransform == null) return; + + string unit = BoundMetaData?.UnitLabelName; + _unitTransform.gameObject.SetActive(!string.IsNullOrEmpty(unit)); + } + // ============ 验证状态回调 ============ /// @@ -224,16 +233,6 @@ namespace XericUI.ReflectionUIGenerator // 刷新标签文本(警告/无效标记在标签上) if (_fieldLabelText != null) _fieldLabelText.text = BuildLabel(state); - - // 值文本颜色 - if (_valueDisplayText != null) - { - _valueDisplayText.color = state == ValidationState.Invalid - ? Color.red - : state == ValidationState.Warning - ? new Color(1f, 0.92f, 0.016f) - : Color.white; - } } // ============ 元数据广播 ============ From fb78769170e6e1be179b7585463d390ab38ada3b Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sun, 26 Jul 2026 10:43:28 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/Form.meta | 8 + Editor/Form/UIFormControllerEditor.cs | 145 ++++ Editor/Form/UIFormControllerEditor.cs.meta | 11 + Editor/Form/UITitleBarEditor.cs | 36 + Editor/Form/UITitleBarEditor.cs.meta | 11 + Runtime/Form/Controllers.meta | 8 + Runtime/Form/Controllers/AutoHideDirection.cs | 14 + .../Controllers/AutoHideDirection.cs.meta | 11 + Runtime/Form/Controllers/IUIFormTarget.cs | 39 ++ .../Form/Controllers/IUIFormTarget.cs.meta | 11 + Runtime/Form/Controllers/UIFormController.cs | 647 ++++++++++++++++++ .../Form/Controllers/UIFormController.cs.meta | 11 + Runtime/Form/Controllers/UITitleBar.cs | 131 ++++ Runtime/Form/Controllers/UITitleBar.cs.meta | 11 + Runtime/Form/Controllers/WindowState.cs | 20 + Runtime/Form/Controllers/WindowState.cs.meta | 11 + Runtime/Form/UIFormManager.cs | 290 ++++++++ Runtime/Form/UIFormManager.cs.meta | 11 + 18 files changed, 1426 insertions(+) create mode 100644 Editor/Form.meta create mode 100644 Editor/Form/UIFormControllerEditor.cs create mode 100644 Editor/Form/UIFormControllerEditor.cs.meta create mode 100644 Editor/Form/UITitleBarEditor.cs create mode 100644 Editor/Form/UITitleBarEditor.cs.meta create mode 100644 Runtime/Form/Controllers.meta create mode 100644 Runtime/Form/Controllers/AutoHideDirection.cs create mode 100644 Runtime/Form/Controllers/AutoHideDirection.cs.meta create mode 100644 Runtime/Form/Controllers/IUIFormTarget.cs create mode 100644 Runtime/Form/Controllers/IUIFormTarget.cs.meta create mode 100644 Runtime/Form/Controllers/UIFormController.cs create mode 100644 Runtime/Form/Controllers/UIFormController.cs.meta create mode 100644 Runtime/Form/Controllers/UITitleBar.cs create mode 100644 Runtime/Form/Controllers/UITitleBar.cs.meta create mode 100644 Runtime/Form/Controllers/WindowState.cs create mode 100644 Runtime/Form/Controllers/WindowState.cs.meta create mode 100644 Runtime/Form/UIFormManager.cs create mode 100644 Runtime/Form/UIFormManager.cs.meta diff --git a/Editor/Form.meta b/Editor/Form.meta new file mode 100644 index 0000000..7366297 --- /dev/null +++ b/Editor/Form.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2f18f75958efeb8428a4689e959e4350 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Form/UIFormControllerEditor.cs b/Editor/Form/UIFormControllerEditor.cs new file mode 100644 index 0000000..fb99b95 --- /dev/null +++ b/Editor/Form/UIFormControllerEditor.cs @@ -0,0 +1,145 @@ +using UnityEditor; +using UnityEngine; + +namespace XericUIEditor.Form +{ + [CustomEditor(typeof(XericUI.Form.UIFormController))] + public class UIFormControllerEditor : Editor + { + private XericUI.Form.UIFormController _controller; + + private void OnEnable() + { + _controller = (XericUI.Form.UIFormController)target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + DrawBasicSection(); + DrawEdgeSection(); + DrawDebugSection(); + + serializedObject.ApplyModifiedProperties(); + } + + #region 基本设置 + + private void DrawBasicSection() + { + EditorGUILayout.LabelField("窗口基本设置", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_WindowTitle"), new GUIContent("窗口标题 (Title)")); + EditorGUILayout.Space(); + } + + #endregion + + #region 边缘行为 + + private void DrawEdgeSection() + { + EditorGUILayout.LabelField("边缘吸附", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_EnableAutoSnap"), new GUIContent("启用边缘吸附 (Auto Snap)")); + if (_controller.AutoSnapEnabled) + { + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_SnapThreshold"), new GUIContent("吸附阈值 (Snap Threshold, px)")); + } + + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("自动隐藏", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_EnableAutoHide"), new GUIContent("启用自动隐藏 (Auto Hide)")); + if (_controller.AutoHideEnabled) + { + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoHideDirection"), new GUIContent("隐藏方向 (Direction)")); + } + + EditorGUILayout.Space(); + } + + #endregion + + #region 调试与测试 + + private void DrawDebugSection() + { + EditorGUILayout.LabelField("调试", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_DebugMode"), new GUIContent("调试模式 (Debug Mode)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_DebugLogEvents"), new GUIContent("事件日志 (Log Events)")); + + EditorGUILayout.Space(); + + // 当前状态 + EditorGUILayout.LabelField("当前状态", EditorStyles.boldLabel); + + var state = _controller.CurrentState; + Color stateColor; + string stateText; + + switch (state) + { + case XericUI.Form.WindowState.Normal: + stateColor = Color.white; + stateText = "● 窗口化 (Normal)"; + break; + case XericUI.Form.WindowState.Maximized: + stateColor = Color.green; + stateText = "■ 全屏 (Maximized)"; + break; + case XericUI.Form.WindowState.Minimized: + stateColor = Color.gray; + stateText = "○ 最小化 (Minimized)"; + break; + case XericUI.Form.WindowState.EdgeHidden: + stateColor = new Color(1f, 0.6f, 0f); + stateText = "◄ 边缘隐藏 (Edge Hidden)"; + break; + default: + stateColor = Color.white; + stateText = state.ToString(); + break; + } + + GUI.color = stateColor; + EditorGUILayout.LabelField(stateText, EditorStyles.miniBoldLabel); + GUI.color = Color.white; + + EditorGUILayout.Space(); + + // 手动测试按钮 + EditorGUILayout.LabelField("手动测试", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("全屏/还原")) + { + _controller.ToggleMaximize(); + SceneView.RepaintAll(); + } + if (GUILayout.Button("模拟进入")) + { + _controller.SimulateEnter(); + SceneView.RepaintAll(); + } + if (GUILayout.Button("模拟退出")) + { + _controller.SimulateExit(); + SceneView.RepaintAll(); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + + // 调试提示 + if (!Application.isPlaying && _controller.DebugMode) + { + EditorGUILayout.HelpBox( + "调试模式已启用 — 在 SceneView 中移动鼠标即可预览。\n" + + "蓝色边框 = 窗口范围 | 绿色 = 鼠标悬停中 | 橙色 = 吸附阈值范围", + MessageType.Info); + } + } + + #endregion + } +} diff --git a/Editor/Form/UIFormControllerEditor.cs.meta b/Editor/Form/UIFormControllerEditor.cs.meta new file mode 100644 index 0000000..9488ea3 --- /dev/null +++ b/Editor/Form/UIFormControllerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f105e0b75b54e142b3844d8f657e32a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Form/UITitleBarEditor.cs b/Editor/Form/UITitleBarEditor.cs new file mode 100644 index 0000000..e458dd0 --- /dev/null +++ b/Editor/Form/UITitleBarEditor.cs @@ -0,0 +1,36 @@ +using UnityEditor; +using UnityEngine; + +namespace XericUIEditor.Form +{ + [CustomEditor(typeof(XericUI.Form.UITitleBar))] + public class UITitleBarEditor : Editor + { + private XericUI.Form.UITitleBar _titleBar; + + private void OnEnable() + { + _titleBar = (XericUI.Form.UITitleBar)target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.LabelField("标题栏控件引用", EditorStyles.boldLabel); + EditorGUILayout.HelpBox( + "将标题栏下的子控件拖入下方槽位即可。\n" + + "UITitleBar 会在 Start 时自动向上查找父级的 UIFormController 并绑定事件。", + MessageType.Info); + + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_TitleText"), new GUIContent("标题文本 (Title Text)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CloseButton"), new GUIContent("关闭按钮 (Close)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MaximizeButton"), new GUIContent("最大化按钮 (Maximize)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MinimizeButton"), new GUIContent("最小化按钮 (Minimize)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoSnapToggle"), new GUIContent("吸附开关 (Auto Snap)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoHideToggle"), new GUIContent("隐藏开关 (Auto Hide)")); + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Form/UITitleBarEditor.cs.meta b/Editor/Form/UITitleBarEditor.cs.meta new file mode 100644 index 0000000..abe633b --- /dev/null +++ b/Editor/Form/UITitleBarEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c45b2d7be6212e479b5cf00a0369596 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers.meta b/Runtime/Form/Controllers.meta new file mode 100644 index 0000000..5b0f65a --- /dev/null +++ b/Runtime/Form/Controllers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d1e580c430b3308419647d63c73b21c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/AutoHideDirection.cs b/Runtime/Form/Controllers/AutoHideDirection.cs new file mode 100644 index 0000000..c3dab5c --- /dev/null +++ b/Runtime/Form/Controllers/AutoHideDirection.cs @@ -0,0 +1,14 @@ +namespace XericUI.Form +{ + /// + /// 自动隐藏滑动方向。 + /// 窗口贴边后可沿此方向滑出屏幕,鼠标进入对应边缘区域后滑回。 + /// + public enum AutoHideDirection + { + Left, + Right, + Up, + Down + } +} diff --git a/Runtime/Form/Controllers/AutoHideDirection.cs.meta b/Runtime/Form/Controllers/AutoHideDirection.cs.meta new file mode 100644 index 0000000..a26cdbd --- /dev/null +++ b/Runtime/Form/Controllers/AutoHideDirection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6441e2d1b03e7a942a1c958f979e9d3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/IUIFormTarget.cs b/Runtime/Form/Controllers/IUIFormTarget.cs new file mode 100644 index 0000000..5d50570 --- /dev/null +++ b/Runtime/Form/Controllers/IUIFormTarget.cs @@ -0,0 +1,39 @@ +using UnityEngine; + +namespace XericUI.Form +{ + /// + /// 窗体悬停目标接口。 + /// 实现此接口的组件可注册到 UIFormManager,由管理器统一检测鼠标悬停状态。 + /// + public interface IUIFormTarget + { + /// + /// 进入范围(屏幕空间 Rect,原点为左下角,单位为像素)。 + /// 鼠标进入此范围时触发 OnPointerEnter。 + /// + Rect GetEnterRect(); + + /// + /// 退出范围(屏幕空间 Rect,原点为左下角,单位为像素)。 + /// 应大于等于 EnterRect,鼠标离开此范围时触发 OnPointerExit,避免边界抖动。 + /// + Rect GetExitRect(); + + /// + /// 鼠标进入范围内时由 UIFormManager 调用。 + /// + void OnPointerEnter(); + + /// + /// 鼠标离开退出范围时由 UIFormManager 调用。 + /// + void OnPointerExit(); + + /// + /// 此目标是否处于激活状态。 + /// 返回 false 时管理器跳过此目标的检测。 + /// + bool IsActive { get; } + } +} diff --git a/Runtime/Form/Controllers/IUIFormTarget.cs.meta b/Runtime/Form/Controllers/IUIFormTarget.cs.meta new file mode 100644 index 0000000..94bc4cc --- /dev/null +++ b/Runtime/Form/Controllers/IUIFormTarget.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 756fa65ea23a49d4ea270f1697aaf89f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/UIFormController.cs b/Runtime/Form/Controllers/UIFormController.cs new file mode 100644 index 0000000..7ba8998 --- /dev/null +++ b/Runtime/Form/Controllers/UIFormController.cs @@ -0,0 +1,647 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XericUI.Form +{ + #region 枚举与结构体 + + /// 范围坐标空间枚举 + public enum FormRangeSpace + { + RelativeOffset, + CanvasLocal, + AnchorToSelf, + AnchorToCanvas + } + + [System.Serializable] + public struct RangeRect + { + [Tooltip("坐标空间")] + public FormRangeSpace space; + + [Tooltip("范围定义。\nRelativeOffset: x,y=像素偏移; w,h=像素宽高(0=用参考尺寸)\nCanvasLocal: Canvas局部坐标\nAnchorToSelf: x,y=锚点0-1; w,h=尺寸比例0-1\nAnchorToCanvas: x,y=锚点0-1; w,h=尺寸比例0-1")] + public Rect rect; + } + + #endregion + + /// + /// UI 窗体控制器 — 挂载到窗口根 GameObject 上,自身即为完整窗口。 + /// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动。 + /// 标题栏功能由独立的 UITitleBar 子组件提供,Controller 只提供公开 API。 + /// + [AddComponentMenu("Xeric UI Vessel/Form/UI Form Controller")] + [RequireComponent(typeof(RectTransform))] + public class UIFormController : MonoBehaviour, IUIFormTarget, IBeginDragHandler, IDragHandler, IEndDragHandler + { + #region 序列化字段 + + [Header("窗口基本设置")] + [SerializeField, Tooltip("窗口标题,供外部读取")] + private string m_WindowTitle = "Window"; + + [Header("边缘吸附")] + [SerializeField, Tooltip("是否启用边缘自动吸附")] + private bool m_EnableAutoSnap = true; + + [SerializeField, Tooltip("吸附阈值(像素),窗口距边缘小于此值时自动贴边")] + private float m_SnapThreshold = 8f; + + [Header("自动隐藏")] + [SerializeField, Tooltip("是否启用自动隐藏(贴边后滑出屏幕)")] + private bool m_EnableAutoHide; + + [SerializeField, Tooltip("自动隐藏的滑动方向")] + private AutoHideDirection m_AutoHideDirection = AutoHideDirection.Left; + + [Header("调试")] + [SerializeField, Tooltip("编辑器调试模式")] + private bool m_DebugMode = true; + + [SerializeField, Tooltip("输出事件日志")] + private bool m_DebugLogEvents; + + #endregion + + #region 公开属性 + + public string WindowTitle => m_WindowTitle; + + /// 是否处于全屏状态 + public bool IsMaximized => _windowState == WindowState.Maximized; + + /// 是否处于边缘隐藏状态 + public bool IsEdgeHidden => _windowState == WindowState.EdgeHidden; + + /// 当前窗口状态 + public WindowState CurrentState => _windowState; + + /// 自动吸附是否开启 + public bool AutoSnapEnabled => m_EnableAutoSnap; + + /// 自动隐藏是否开启 + public bool AutoHideEnabled => m_EnableAutoHide; + + /// 自动隐藏方向 + public AutoHideDirection HideDirection => m_AutoHideDirection; + + /// 编辑器调试模式 + public bool DebugMode => m_DebugMode; + + #endregion + + #region 内部状态 + + private RectTransform _rectTransform; + private Canvas _canvas; + private RectTransform _canvasRt; + + private WindowState _windowState = WindowState.Normal; + + // 拖拽 + private Vector2 _dragOffset; + + // 全屏还原数据 + private Vector2 _savePos; + private Vector2 _saveSize; + private Vector2 _saveAnchorMin; + private Vector2 _saveAnchorMax; + + // 边缘隐藏 + private Vector2 _edgeVisiblePos; + private Vector2 _edgeHiddenPos; + private Coroutine _autoHideCoroutine; + + // 悬停 + private bool _isHovering; + + #endregion + + #region 生命周期 + + protected virtual void Awake() + { + _rectTransform = (RectTransform)transform; + _canvas = GetComponentInParent(); + _canvasRt = _canvas != null ? (RectTransform)_canvas.transform : null; + } + + protected virtual void OnEnable() + { + if (UIFormManager.Main != null) + UIFormManager.Main.RegisterWindow(this); + } + + protected virtual void OnDisable() + { + StopAutoHideMonitor(); + + if (_windowState == WindowState.EdgeHidden && UIFormManager.Main != null) + UIFormManager.Main.UnregisterEdgeHidden(this); + + if (UIFormManager.Main != null) + { + UIFormManager.Main.UnregisterWindow(this); + UIFormManager.Main.UnregisterEdgeHidden(this); + } + + UnsubscribeEditorUpdate(); + _isHovering = false; + } + + #endregion + + #region IUIFormTarget 实现 + + public bool IsActive => isActiveAndEnabled; + + public Rect GetEnterRect() => GetWindowScreenRect(); + public Rect GetExitRect() => GetWindowScreenRect(); + + public void OnPointerEnter() + { + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 鼠标进入窗口范围: {name}", this); + } + + public void OnPointerExit() + { + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 鼠标离开窗口范围: {name}", this); + } + + #endregion + + #region === 公开 API(供 UITitleBar 及外部调用) === + + /// 全屏/还原切换 + public void ToggleMaximize() + { + if (IsMaximized) + RestoreFromFullscreen(); + else + MaximizeToFullscreen(); + } + + /// 最小化窗口 + public void Minimize() + { + if (_windowState == WindowState.Minimized) return; + if (UIFormManager.Main == null) return; + + UIFormManager.Main.MinimizeWindow(this); + } + + /// 关闭窗口(Destroy GameObject) + public void Close() + { + if (UIFormManager.Main != null) + UIFormManager.Main.CloseWindow(this); + + if (Application.isPlaying) + Destroy(gameObject); + else + DestroyImmediate(gameObject); + } + + /// 设置边缘吸附开关 + public void SetAutoSnapEnabled(bool enabled) + { + m_EnableAutoSnap = enabled; + } + + /// 设置自动隐藏开关 + public void SetAutoHideEnabled(bool enabled) + { + m_EnableAutoHide = enabled; + + if (!enabled && _windowState == WindowState.EdgeHidden) + { + // 关闭自动隐藏 → 还原窗口 + SlideInFromEdge(); + } + } + + /// 设置自动隐藏方向 + public void SetAutoHideDirection(AutoHideDirection direction) + { + m_AutoHideDirection = direction; + } + + #endregion + + #region === 全屏/还原 === + + private void MaximizeToFullscreen() + { + if (_windowState == WindowState.Maximized) return; + + _savePos = _rectTransform.anchoredPosition; + _saveSize = _rectTransform.sizeDelta; + _saveAnchorMin = _rectTransform.anchorMin; + _saveAnchorMax = _rectTransform.anchorMax; + + _rectTransform.anchorMin = Vector2.zero; + _rectTransform.anchorMax = Vector2.one; + _rectTransform.offsetMin = Vector2.zero; + _rectTransform.offsetMax = Vector2.zero; + + _windowState = WindowState.Maximized; + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 窗口全屏: {name}", this); + } + + private void RestoreFromFullscreen() + { + if (_windowState != WindowState.Maximized) return; + + _rectTransform.anchorMin = _saveAnchorMin; + _rectTransform.anchorMax = _saveAnchorMax; + _rectTransform.anchoredPosition = _savePos; + _rectTransform.sizeDelta = _saveSize; + + _windowState = WindowState.Normal; + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 窗口还原: {name}", this); + } + + #endregion + + #region === 拖拽 === + + public void OnBeginDrag(PointerEventData eventData) + { + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) + return; + + // 全屏拖拽 → 先还原 + if (IsMaximized) + { + RestoreFromFullscreen(); + // 将窗口移到鼠标附近 + _rectTransform.anchoredPosition = eventData.position; + } + + _dragOffset = _rectTransform.anchoredPosition - eventData.position; + } + + public void OnDrag(PointerEventData eventData) + { + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) + return; + + _rectTransform.anchoredPosition = eventData.position + _dragOffset; + + if (m_EnableAutoSnap) + CheckEdgeSnap(); + } + + public void OnEndDrag(PointerEventData eventData) + { + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) + return; + + // 拖拽结束 → 如果贴边且启用自动隐藏,则触发 + if (m_EnableAutoHide && IsSnappedToEdge()) + { + AutoHideToEdge(); + } + } + + #endregion + + #region === 边缘吸附 === + + private void CheckEdgeSnap() + { + if (_canvasRt == null) return; + + Vector2 canvasSize = _canvasRt.rect.size; + Vector2 pos = _rectTransform.anchoredPosition; + Vector2 size = _rectTransform.rect.size; + + bool snapped = false; + + // 左 + if (Mathf.Abs(pos.x) < m_SnapThreshold) + { + pos.x = 0; + snapped = true; + } + // 右 + else if (Mathf.Abs(pos.x + size.x - canvasSize.x) < m_SnapThreshold) + { + pos.x = canvasSize.x - size.x; + snapped = true; + } + + _rectTransform.anchoredPosition = pos; + } + + /// 检测窗口是否贴边 + private bool IsSnappedToEdge() + { + if (_canvasRt == null) return false; + + Vector2 canvasSize = _canvasRt.rect.size; + Vector2 pos = _rectTransform.anchoredPosition; + Vector2 size = _rectTransform.rect.size; + + return Mathf.Abs(pos.x) < 1f + || Mathf.Abs(pos.x + size.x - canvasSize.x) < 1f; + } + + /// 获取最近的边缘方向 + public AutoHideDirection GetClosestEdge() + { + if (_canvasRt == null) return AutoHideDirection.Left; + + Vector2 canvasSize = _canvasRt.rect.size; + Vector2 pos = _rectTransform.anchoredPosition; + Vector2 size = _rectTransform.rect.size; + + float distLeft = Mathf.Abs(pos.x); + float distRight = Mathf.Abs(pos.x + size.x - canvasSize.x); + + return distLeft < distRight ? AutoHideDirection.Left : AutoHideDirection.Right; + } + + #endregion + + #region === 自动隐藏 === + + private void AutoHideToEdge() + { + if (_windowState == WindowState.EdgeHidden) return; + if (_canvasRt == null) return; + + Vector2 canvasSize = _canvasRt.rect.size; + Vector2 pos = _rectTransform.anchoredPosition; + Vector2 size = _rectTransform.rect.size; + + _edgeVisiblePos = pos; + + AutoHideDirection dir = m_AutoHideDirection; + if (dir == AutoHideDirection.Left || dir == AutoHideDirection.Right) + dir = GetClosestEdge(); + + switch (dir) + { + case AutoHideDirection.Left: + _edgeHiddenPos = new Vector2(-size.x, pos.y); + break; + case AutoHideDirection.Right: + _edgeHiddenPos = new Vector2(canvasSize.x, pos.y); + break; + case AutoHideDirection.Down: + _edgeHiddenPos = new Vector2(pos.x, -size.y); + break; + case AutoHideDirection.Up: + _edgeHiddenPos = new Vector2(pos.x, canvasSize.y); + break; + } + + _rectTransform.anchoredPosition = _edgeHiddenPos; + _windowState = WindowState.EdgeHidden; + + if (UIFormManager.Main != null) + UIFormManager.Main.RegisterEdgeHidden(this, dir); + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 自动隐藏 (方向={dir}): {name}", this); + } + + /// 由 Manager 调用 — 从边缘滑出 + public void SlideInFromEdge() + { + if (_windowState != WindowState.EdgeHidden) return; + + StopAutoHideMonitor(); + _rectTransform.anchoredPosition = _edgeVisiblePos; + _windowState = WindowState.Normal; + + if (UIFormManager.Main != null) + UIFormManager.Main.UnregisterEdgeHidden(this); + + // 开始监控鼠标是否离开窗口 + _autoHideCoroutine = StartCoroutine(MonitorMouseExit()); + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 从边缘滑出: {name}", this); + } + + /// 监控鼠标是否离开窗口范围,离开则自动隐藏 + private IEnumerator MonitorMouseExit() + { + while (_windowState == WindowState.Normal && m_EnableAutoHide) + { + yield return null; + + Rect screenRect = GetWindowScreenRect(); + if (!screenRect.Contains(Input.mousePosition)) + { + AutoHideToEdge(); + yield break; + } + } + } + + private void StopAutoHideMonitor() + { + if (_autoHideCoroutine != null) + { + StopCoroutine(_autoHideCoroutine); + _autoHideCoroutine = null; + } + } + + #endregion + + #region === 窗口屏幕范围 === + + /// 获取窗口自身在屏幕空间中的 Rect + public Rect GetWindowScreenRect() + { + if (_rectTransform == null || _canvas == null) return Rect.zero; + + Vector3[] corners = new Vector3[4]; + _rectTransform.GetWorldCorners(corners); + + Camera cam = _canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera; + + Vector2 min = Vector2.positiveInfinity; + Vector2 max = Vector2.negativeInfinity; + + for (int i = 0; i < 4; i++) + { + Vector2 sp = cam != null + ? RectTransformUtility.WorldToScreenPoint(cam, corners[i]) + : (Vector2)corners[i]; + + min = Vector2.Min(min, sp); + max = Vector2.Max(max, sp); + } + + return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); + } + + #endregion + + #region === 悬停检测(供 Manager 调用) === + + public bool CheckHover(Vector2 screenPoint) + { + Rect windowRect = GetWindowScreenRect(); + + if (!_isHovering) + { + if (windowRect.Contains(screenPoint)) + { + _isHovering = true; + OnPointerEnter(); + } + } + else + { + if (!windowRect.Contains(screenPoint)) + { + _isHovering = false; + OnPointerExit(); + } + } + + return _isHovering; + } + + public void SimulateEnter() + { + _isHovering = true; + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 模拟进入: {name}", this); + } + + public void SimulateExit() + { + _isHovering = false; + + if (m_DebugLogEvents) + Debug.Log($"[UIFormController] 模拟退出: {name}", this); + } + + #endregion + + #region 编辑器调试 + +#if UNITY_EDITOR + + [System.NonSerialized] private bool _editorDebugSubscribed; + [System.NonSerialized] private Vector2 _editorMouseScreenPos; + + private void SubscribeEditorUpdate() + { + if (_editorDebugSubscribed) return; + UnityEditor.SceneView.duringSceneGui += OnSceneGUI; + _editorDebugSubscribed = true; + } + + private void UnsubscribeEditorUpdate() + { + if (!_editorDebugSubscribed) return; + UnityEditor.SceneView.duringSceneGui -= OnSceneGUI; + _editorDebugSubscribed = false; + } + + protected virtual void OnValidate() + { + if (m_DebugMode) + SubscribeEditorUpdate(); + else + UnsubscribeEditorUpdate(); + } + + /// + /// SceneView 重绘回调 — 在 GUI 上下文中获取鼠标屏幕坐标并执行悬停检测。 + /// 使用 GUIUtility.GUIToScreenPoint 将 SceneView 内的 GUI 坐标转为 Unity 屏幕坐标。 + /// + private void OnSceneGUI(UnityEditor.SceneView sceneView) + { + if (this == null) + { + UnsubscribeEditorUpdate(); + return; + } + + if (Application.isPlaying) return; + if (!m_DebugMode) return; + + // Event.current.mousePosition 是相对于 SceneView 窗口的 GUI 坐标 + // GUIUtility.GUIToScreenPoint 将其转换为屏幕坐标(左下角为原点) + Vector2 guiPos = Event.current.mousePosition; + _editorMouseScreenPos = GUIUtility.GUIToScreenPoint(guiPos); + + CheckHover(_editorMouseScreenPos); + } + + protected virtual void OnDrawGizmosSelected() + { + if (!m_DebugMode) return; + if (_canvas == null) return; + if (_canvasRt == null) return; + + Rect screenRect = GetWindowScreenRect(); + if (screenRect.width <= 0 || screenRect.height <= 0) return; + + // 悬停时高亮边框 + Color borderColor = _isHovering + ? new Color(0f, 1f, 0f, 0.6f) + : new Color(0f, 0.7f, 1f, 0.3f); + Gizmos.color = borderColor; + + DrawScreenRectOnCanvas(screenRect, borderColor, _canvas, _canvasRt); + + // 边缘吸附阈值可视化 + if (m_EnableAutoSnap) + { + Gizmos.color = new Color(1f, 0.6f, 0f, 0.15f); + DrawScreenRectOnCanvas( + new Rect(screenRect.x + m_SnapThreshold, screenRect.y, + screenRect.width - m_SnapThreshold * 2, screenRect.height), + Gizmos.color, _canvas, _canvasRt); + } + } + + private static void DrawScreenRectOnCanvas(Rect screenRect, Color color, Canvas canvas, RectTransform canvasRt) + { + if (screenRect.width <= 0 || screenRect.height <= 0) return; + + Gizmos.color = color; + Camera uiCam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; + + Vector3 p0 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMin), uiCam, canvasRt); + Vector3 p1 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMin), uiCam, canvasRt); + Vector3 p2 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMax), uiCam, canvasRt); + Vector3 p3 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMax), uiCam, canvasRt); + + Gizmos.DrawLine(p0, p1); + Gizmos.DrawLine(p1, p2); + Gizmos.DrawLine(p2, p3); + Gizmos.DrawLine(p3, p0); + } + + private static Vector3 ScreenToWorldOnCanvas(Vector3 screenPoint, Camera uiCamera, RectTransform canvasRt) + { + RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRt, (Vector2)screenPoint, uiCamera, out Vector3 wp); + return wp; + } + +#endif + + #endregion + } +} diff --git a/Runtime/Form/Controllers/UIFormController.cs.meta b/Runtime/Form/Controllers/UIFormController.cs.meta new file mode 100644 index 0000000..0d4bbd5 --- /dev/null +++ b/Runtime/Form/Controllers/UIFormController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffb69c61a83ce7b4ca1eadba0f145096 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/UITitleBar.cs b/Runtime/Form/Controllers/UITitleBar.cs new file mode 100644 index 0000000..52b3b48 --- /dev/null +++ b/Runtime/Form/Controllers/UITitleBar.cs @@ -0,0 +1,131 @@ +#if dUI_TextMeshPro +using UI_Text = TMPro.TextMeshProUGUI; +#else +using UI_Text = UnityEngine.UI.Text; +#endif + +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace XericUI.Form +{ + /// + /// 窗口标题栏 — 独立子组件,挂载在 UIFormController 的子对象上。 + /// Start 时向上查找父级 UIFormController,绑定按钮事件到其公开 API。 + /// 与窗口完全解耦:Controller 不关心标题栏是否存在。 + /// + [AddComponentMenu("Xeric UI Vessel/Form/UI Title Bar")] + public class UITitleBar : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler + { + [Header("控件引用")] + [SerializeField, Tooltip("标题文本")] + private UI_Text m_TitleText; + + [SerializeField, Tooltip("关闭按钮")] + private Button m_CloseButton; + + [SerializeField, Tooltip("最大化按钮")] + private Button m_MaximizeButton; + + [SerializeField, Tooltip("最小化按钮")] + private Button m_MinimizeButton; + + [SerializeField, Tooltip("自动吸附 Toggle")] + private Toggle m_AutoSnapToggle; + + [SerializeField, Tooltip("自动隐藏 Toggle")] + private Toggle m_AutoHideToggle; + + private UIFormController _controller; + + private void Start() + { + _controller = GetComponentInParent(); + + if (_controller == null) + { + Debug.LogWarning($"[UITitleBar] 未在父级找到 UIFormController: {name}", this); + return; + } + + BindEvents(); + RefreshUI(); + } + + private void Update() + { + if (_controller == null) return; + + // 实时同步标题(可能由外部修改) + if (m_TitleText != null && m_TitleText.text != _controller.WindowTitle) + m_TitleText.text = _controller.WindowTitle; + } + + #region 事件绑定 + + private void BindEvents() + { + if (m_CloseButton != null) + m_CloseButton.onClick.AddListener(() => _controller.Close()); + + if (m_MaximizeButton != null) + m_MaximizeButton.onClick.AddListener(() => _controller.ToggleMaximize()); + + if (m_MinimizeButton != null) + m_MinimizeButton.onClick.AddListener(() => _controller.Minimize()); + + if (m_AutoSnapToggle != null) + { + m_AutoSnapToggle.isOn = _controller.AutoSnapEnabled; + m_AutoSnapToggle.onValueChanged.AddListener(v => _controller.SetAutoSnapEnabled(v)); + } + + if (m_AutoHideToggle != null) + { + m_AutoHideToggle.isOn = _controller.AutoHideEnabled; + m_AutoHideToggle.onValueChanged.AddListener(v => _controller.SetAutoHideEnabled(v)); + } + } + + #endregion + + #region 拖拽转发 + + public void OnBeginDrag(PointerEventData eventData) + { + if (_controller != null) + _controller.OnBeginDrag(eventData); + } + + public void OnDrag(PointerEventData eventData) + { + if (_controller != null) + _controller.OnDrag(eventData); + } + + public void OnEndDrag(PointerEventData eventData) + { + if (_controller != null) + _controller.OnEndDrag(eventData); + } + + #endregion + + #region UI 刷新 + + private void RefreshUI() + { + if (m_TitleText != null) + m_TitleText.text = _controller.WindowTitle; + + if (m_AutoSnapToggle != null) + m_AutoSnapToggle.isOn = _controller.AutoSnapEnabled; + + if (m_AutoHideToggle != null) + m_AutoHideToggle.isOn = _controller.AutoHideEnabled; + } + + #endregion + } +} diff --git a/Runtime/Form/Controllers/UITitleBar.cs.meta b/Runtime/Form/Controllers/UITitleBar.cs.meta new file mode 100644 index 0000000..6fb4967 --- /dev/null +++ b/Runtime/Form/Controllers/UITitleBar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5e6e4624592b3248a4c65c33cd21d5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/WindowState.cs b/Runtime/Form/Controllers/WindowState.cs new file mode 100644 index 0000000..3c7ab03 --- /dev/null +++ b/Runtime/Form/Controllers/WindowState.cs @@ -0,0 +1,20 @@ +namespace XericUI.Form +{ + /// + /// 窗口当前状态。 + /// + public enum WindowState + { + /// 普通窗口化 + Normal, + + /// 全屏最大化 + Maximized, + + /// 最小化(隐藏并保存在 Manager 中) + Minimized, + + /// 边缘自动隐藏(贴边滑出屏幕) + EdgeHidden + } +} diff --git a/Runtime/Form/Controllers/WindowState.cs.meta b/Runtime/Form/Controllers/WindowState.cs.meta new file mode 100644 index 0000000..f0e9ec8 --- /dev/null +++ b/Runtime/Form/Controllers/WindowState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 692b97f1c71f9414b8bb68dd6ed33718 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/UIFormManager.cs b/Runtime/Form/UIFormManager.cs new file mode 100644 index 0000000..f02d048 --- /dev/null +++ b/Runtime/Form/UIFormManager.cs @@ -0,0 +1,290 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XericUI.Form +{ + /// + /// UI 窗体管理器 — 窗口管理中心。 + /// 挂载在 Canvas 所在 GameObject 上(或通过代码自动创建), + /// 管理所有窗口的注册、最小化恢复、边缘触发隐藏窗口的显隐调度。 + /// + [AddComponentMenu("Xeric UI Vessel/Form/UI Form Manager")] + [DefaultExecutionOrder(32000)] + public class UIFormManager : MonoBehaviour + { + [Header("边缘触发")] + [SerializeField, Tooltip("边缘触发区域大小(x=宽度方向,y=高度方向),鼠标进入此区域时触发对应方向隐藏窗口的显示")] + private Vector2 m_EdgeTriggerSize = new Vector2(80, 60); + + [Header("调试")] + [SerializeField, Tooltip("输出事件日志")] + private bool m_EnableDebugLog; + + #region 静态单例 + + private static UIFormManager s_Main; + + /// 全局单例引用 + public static UIFormManager Main => s_Main; + + #endregion + + #region 内部状态 + + // 所有窗口 + private readonly List _windows = new List(); + + // 最小化窗口数据 + private struct MinimizedData + { + public Vector2 anchoredPosition; + public Vector2 sizeDelta; + public Vector2 anchorMin; + public Vector2 anchorMax; + } + private readonly Dictionary _minimized = new(); + + // 边缘隐藏窗口(按方向分组) + private readonly Dictionary> _edgeHidden = new() + { + { AutoHideDirection.Left, new List() }, + { AutoHideDirection.Right, new List() }, + { AutoHideDirection.Up, new List() }, + { AutoHideDirection.Down, new List() }, + }; + + // 边缘触发状态跟踪(避免重复触发) + private AutoHideDirection? _lastEdgeTrigger; + + #endregion + + #region 生命周期 + + protected virtual void Awake() + { + if (s_Main != null && s_Main != this) + { + Debug.LogWarning($"[UIFormManager] 已存在单例,销毁重复实例: {name}", this); + Destroy(this); + return; + } + + s_Main = this; + } + + protected virtual void OnEnable() + { + if (s_Main == null) s_Main = this; + } + + protected virtual void OnDisable() + { + if (s_Main == this) s_Main = null; + } + + protected virtual void OnDestroy() + { + if (s_Main == this) s_Main = null; + _windows.Clear(); + _minimized.Clear(); + foreach (var list in _edgeHidden.Values) list.Clear(); + } + + protected virtual void Update() + { + CheckEdgeTriggers(); + } + + #endregion + + #region === 窗口注册 / 注销 === + + public void RegisterWindow(UIFormController window) + { + if (window == null || _windows.Contains(window)) return; + _windows.Add(window); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 注册窗口: {window.name}"); + } + + public void UnregisterWindow(UIFormController window) + { + if (window == null) return; + _windows.Remove(window); + UnregisterEdgeHidden(window); + _minimized.Remove(window); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 注销窗口: {window.name}"); + } + + #endregion + + #region === 边缘隐藏窗口管理 === + + /// 注册边缘隐藏窗口 + public void RegisterEdgeHidden(UIFormController window, AutoHideDirection direction) + { + if (window == null) return; + + var list = _edgeHidden[direction]; + if (!list.Contains(window)) + list.Add(window); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 边缘隐藏注册: {window.name} (方向={direction})"); + } + + /// 注销边缘隐藏窗口 + public void UnregisterEdgeHidden(UIFormController window) + { + if (window == null) return; + + foreach (var list in _edgeHidden.Values) + list.Remove(window); + } + + /// 获取指定方向的隐藏窗口列表 + public IReadOnlyList GetEdgeWindows(AutoHideDirection direction) + { + return _edgeHidden[direction]; + } + + #endregion + + #region === 边缘触发检测 === + + private void CheckEdgeTriggers() + { + Vector2 mousePos = Input.mousePosition; + float sw = Screen.width; + float sh = Screen.height; + + bool leftTrigger = mousePos.x < m_EdgeTriggerSize.x; + bool rightTrigger = mousePos.x > (sw - m_EdgeTriggerSize.x); + bool bottomTrigger = mousePos.y < m_EdgeTriggerSize.y; + bool topTrigger = mousePos.y > (sh - m_EdgeTriggerSize.y); + + AutoHideDirection? activeTrigger = null; + + if (leftTrigger) activeTrigger = AutoHideDirection.Left; + else if (rightTrigger) activeTrigger = AutoHideDirection.Right; + else if (bottomTrigger) activeTrigger = AutoHideDirection.Down; + else if (topTrigger) activeTrigger = AutoHideDirection.Up; + + // 触发进入 → 显示该方向窗口 + if (activeTrigger.HasValue && activeTrigger != _lastEdgeTrigger) + { + ShowEdgeWindows(activeTrigger.Value); + } + + // 鼠标离开所有边缘触发区 + if (!activeTrigger.HasValue) + { + _lastEdgeTrigger = null; + } + } + + /// 显示指定方向的所有边缘隐藏窗口 + public void ShowEdgeWindows(AutoHideDirection direction) + { + _lastEdgeTrigger = direction; + + var list = _edgeHidden[direction]; + for (int i = list.Count - 1; i >= 0; i--) + { + var window = list[i]; + if (window == null) + { + list.RemoveAt(i); + continue; + } + + window.SlideInFromEdge(); + } + + if (m_EnableDebugLog && list.Count > 0) + Debug.Log($"[UIFormManager] 边缘触发: 方向={direction}, 显示 {list.Count} 个窗口"); + } + + #endregion + + #region === 最小化 / 恢复 === + + internal void MinimizeWindow(UIFormController window) + { + if (window == null || _minimized.ContainsKey(window)) return; + + var rt = window.transform as RectTransform; + if (rt == null) return; + + _minimized[window] = new MinimizedData + { + anchoredPosition = rt.anchoredPosition, + sizeDelta = rt.sizeDelta, + anchorMin = rt.anchorMin, + anchorMax = rt.anchorMax, + }; + + window.gameObject.SetActive(false); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 最小化窗口: {window.name}"); + } + + /// 恢复已最小化的窗口 + public void RestoreWindow(UIFormController window) + { + if (window == null || !_minimized.TryGetValue(window, out var data)) return; + + var rt = window.transform as RectTransform; + if (rt == null) return; + + rt.anchorMin = data.anchorMin; + rt.anchorMax = data.anchorMax; + rt.anchoredPosition = data.anchoredPosition; + rt.sizeDelta = data.sizeDelta; + + window.gameObject.SetActive(true); + _minimized.Remove(window); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 恢复窗口: {window.name}"); + } + + /// 获取所有已最小化的窗口列表 + public IReadOnlyList GetMinimizedWindows() + { + return new List(_minimized.Keys); + } + + #endregion + + #region === 关闭 === + + internal void CloseWindow(UIFormController window) + { + if (window == null) return; + + _windows.Remove(window); + UnregisterEdgeHidden(window); + _minimized.Remove(window); + + if (m_EnableDebugLog) + Debug.Log($"[UIFormManager] 关闭窗口: {window.name}"); + } + + #endregion + + #region === 查询 === + + /// 已注册窗口数量 + public int WindowCount => _windows.Count; + + /// 获取所有已注册窗口 + public IReadOnlyList GetAllWindows() => _windows; + + #endregion + } +} diff --git a/Runtime/Form/UIFormManager.cs.meta b/Runtime/Form/UIFormManager.cs.meta new file mode 100644 index 0000000..4f49fe7 --- /dev/null +++ b/Runtime/Form/UIFormManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfd535659e1184e42b4d38016ccd70ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 96d6d63a643856790ff32561fd8bb919c35beeaa Mon Sep 17 00:00:00 2001 From: lrc <571244399@qq.com> Date: Sun, 26 Jul 2026 16:48:29 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/Resources/Icon.meta | 8 + Runtime/Resources/Icon/RectBold.png | Bin 0 -> 2639 bytes Runtime/Resources/Icon/RectBold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/RectCur.png | Bin 0 -> 2570 bytes Runtime/Resources/Icon/RectCur.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/RectCurLine.png | Bin 0 -> 2987 bytes Runtime/Resources/Icon/RectCurLine.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/RectLine.png | Bin 0 -> 2461 bytes Runtime/Resources/Icon/RectLine.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/add-bold.png | Bin 0 -> 2262 bytes Runtime/Resources/Icon/add-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-double-left.png | Bin 0 -> 4692 bytes .../Resources/Icon/arrow-double-left.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-double-right.png | Bin 0 -> 4665 bytes .../Icon/arrow-double-right.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-down-bold.png | Bin 0 -> 2526 bytes .../Resources/Icon/arrow-down-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-down-filling.png | Bin 0 -> 2364 bytes .../Icon/arrow-down-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-left-bold.png | Bin 0 -> 3443 bytes .../Resources/Icon/arrow-left-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-left-filling.png | Bin 0 -> 2876 bytes .../Icon/arrow-left-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-right-bold.png | Bin 0 -> 3353 bytes .../Resources/Icon/arrow-right-bold.png.meta | 170 ++++++++++++++++++ .../Resources/Icon/arrow-right-filling.png | Bin 0 -> 2711 bytes .../Icon/arrow-right-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-up-bold.png | Bin 0 -> 2516 bytes Runtime/Resources/Icon/arrow-up-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/arrow-up-filling.png | Bin 0 -> 2301 bytes .../Resources/Icon/arrow-up-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/browse.png | Bin 0 -> 6017 bytes Runtime/Resources/Icon/browse.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/close-bold.png | Bin 0 -> 2946 bytes Runtime/Resources/Icon/close-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/direction-down.png | Bin 0 -> 3429 bytes .../Resources/Icon/direction-down.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/direction-left.png | Bin 0 -> 3253 bytes .../Resources/Icon/direction-left.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/direction-right.png | Bin 0 -> 3244 bytes .../Resources/Icon/direction-right.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/direction-up.png | Bin 0 -> 3362 bytes Runtime/Resources/Icon/direction-up.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/eye-close.png | Bin 0 -> 6053 bytes Runtime/Resources/Icon/eye-close.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/fabulous.png | Bin 0 -> 4627 bytes Runtime/Resources/Icon/fabulous.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/favorite-filling.png | Bin 0 -> 3681 bytes .../Resources/Icon/favorite-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/favorite.png | Bin 0 -> 5200 bytes Runtime/Resources/Icon/favorite.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/folder-close.png | Bin 0 -> 2569 bytes Runtime/Resources/Icon/folder-close.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/folder-filling.png | Bin 0 -> 2436 bytes .../Resources/Icon/folder-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/hide.png | Bin 0 -> 4084 bytes Runtime/Resources/Icon/hide.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/minus-bold.png | Bin 0 -> 1960 bytes Runtime/Resources/Icon/minus-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/more.png | Bin 0 -> 6112 bytes Runtime/Resources/Icon/more.png.meta | 170 ++++++++++++++++++ .../Resources/Icon/notification-filling.png | Bin 0 -> 3180 bytes .../Icon/notification-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/pdf.png | Bin 0 -> 3892 bytes Runtime/Resources/Icon/pdf.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/picture-filling.png | Bin 0 -> 3321 bytes .../Resources/Icon/picture-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/picture.png | Bin 0 -> 3987 bytes Runtime/Resources/Icon/picture.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/save.png | Bin 0 -> 2616 bytes Runtime/Resources/Icon/save.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/select-bold.png | Bin 0 -> 2733 bytes Runtime/Resources/Icon/select-bold.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/setting.png | Bin 0 -> 7322 bytes Runtime/Resources/Icon/setting.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/success-filling.png | Bin 0 -> 4231 bytes .../Resources/Icon/success-filling.png.meta | 170 ++++++++++++++++++ Runtime/Resources/Icon/warning-filling.png | Bin 0 -> 3704 bytes .../Resources/Icon/warning-filling.png.meta | 170 ++++++++++++++++++ 79 files changed, 6638 insertions(+) create mode 100644 Runtime/Resources/Icon.meta create mode 100644 Runtime/Resources/Icon/RectBold.png create mode 100644 Runtime/Resources/Icon/RectBold.png.meta create mode 100644 Runtime/Resources/Icon/RectCur.png create mode 100644 Runtime/Resources/Icon/RectCur.png.meta create mode 100644 Runtime/Resources/Icon/RectCurLine.png create mode 100644 Runtime/Resources/Icon/RectCurLine.png.meta create mode 100644 Runtime/Resources/Icon/RectLine.png create mode 100644 Runtime/Resources/Icon/RectLine.png.meta create mode 100644 Runtime/Resources/Icon/add-bold.png create mode 100644 Runtime/Resources/Icon/add-bold.png.meta create mode 100644 Runtime/Resources/Icon/arrow-double-left.png create mode 100644 Runtime/Resources/Icon/arrow-double-left.png.meta create mode 100644 Runtime/Resources/Icon/arrow-double-right.png create mode 100644 Runtime/Resources/Icon/arrow-double-right.png.meta create mode 100644 Runtime/Resources/Icon/arrow-down-bold.png create mode 100644 Runtime/Resources/Icon/arrow-down-bold.png.meta create mode 100644 Runtime/Resources/Icon/arrow-down-filling.png create mode 100644 Runtime/Resources/Icon/arrow-down-filling.png.meta create mode 100644 Runtime/Resources/Icon/arrow-left-bold.png create mode 100644 Runtime/Resources/Icon/arrow-left-bold.png.meta create mode 100644 Runtime/Resources/Icon/arrow-left-filling.png create mode 100644 Runtime/Resources/Icon/arrow-left-filling.png.meta create mode 100644 Runtime/Resources/Icon/arrow-right-bold.png create mode 100644 Runtime/Resources/Icon/arrow-right-bold.png.meta create mode 100644 Runtime/Resources/Icon/arrow-right-filling.png create mode 100644 Runtime/Resources/Icon/arrow-right-filling.png.meta create mode 100644 Runtime/Resources/Icon/arrow-up-bold.png create mode 100644 Runtime/Resources/Icon/arrow-up-bold.png.meta create mode 100644 Runtime/Resources/Icon/arrow-up-filling.png create mode 100644 Runtime/Resources/Icon/arrow-up-filling.png.meta create mode 100644 Runtime/Resources/Icon/browse.png create mode 100644 Runtime/Resources/Icon/browse.png.meta create mode 100644 Runtime/Resources/Icon/close-bold.png create mode 100644 Runtime/Resources/Icon/close-bold.png.meta create mode 100644 Runtime/Resources/Icon/direction-down.png create mode 100644 Runtime/Resources/Icon/direction-down.png.meta create mode 100644 Runtime/Resources/Icon/direction-left.png create mode 100644 Runtime/Resources/Icon/direction-left.png.meta create mode 100644 Runtime/Resources/Icon/direction-right.png create mode 100644 Runtime/Resources/Icon/direction-right.png.meta create mode 100644 Runtime/Resources/Icon/direction-up.png create mode 100644 Runtime/Resources/Icon/direction-up.png.meta create mode 100644 Runtime/Resources/Icon/eye-close.png create mode 100644 Runtime/Resources/Icon/eye-close.png.meta create mode 100644 Runtime/Resources/Icon/fabulous.png create mode 100644 Runtime/Resources/Icon/fabulous.png.meta create mode 100644 Runtime/Resources/Icon/favorite-filling.png create mode 100644 Runtime/Resources/Icon/favorite-filling.png.meta create mode 100644 Runtime/Resources/Icon/favorite.png create mode 100644 Runtime/Resources/Icon/favorite.png.meta create mode 100644 Runtime/Resources/Icon/folder-close.png create mode 100644 Runtime/Resources/Icon/folder-close.png.meta create mode 100644 Runtime/Resources/Icon/folder-filling.png create mode 100644 Runtime/Resources/Icon/folder-filling.png.meta create mode 100644 Runtime/Resources/Icon/hide.png create mode 100644 Runtime/Resources/Icon/hide.png.meta create mode 100644 Runtime/Resources/Icon/minus-bold.png create mode 100644 Runtime/Resources/Icon/minus-bold.png.meta create mode 100644 Runtime/Resources/Icon/more.png create mode 100644 Runtime/Resources/Icon/more.png.meta create mode 100644 Runtime/Resources/Icon/notification-filling.png create mode 100644 Runtime/Resources/Icon/notification-filling.png.meta create mode 100644 Runtime/Resources/Icon/pdf.png create mode 100644 Runtime/Resources/Icon/pdf.png.meta create mode 100644 Runtime/Resources/Icon/picture-filling.png create mode 100644 Runtime/Resources/Icon/picture-filling.png.meta create mode 100644 Runtime/Resources/Icon/picture.png create mode 100644 Runtime/Resources/Icon/picture.png.meta create mode 100644 Runtime/Resources/Icon/save.png create mode 100644 Runtime/Resources/Icon/save.png.meta create mode 100644 Runtime/Resources/Icon/select-bold.png create mode 100644 Runtime/Resources/Icon/select-bold.png.meta create mode 100644 Runtime/Resources/Icon/setting.png create mode 100644 Runtime/Resources/Icon/setting.png.meta create mode 100644 Runtime/Resources/Icon/success-filling.png create mode 100644 Runtime/Resources/Icon/success-filling.png.meta create mode 100644 Runtime/Resources/Icon/warning-filling.png create mode 100644 Runtime/Resources/Icon/warning-filling.png.meta diff --git a/Runtime/Resources/Icon.meta b/Runtime/Resources/Icon.meta new file mode 100644 index 0000000..5a53d03 --- /dev/null +++ b/Runtime/Resources/Icon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3acab8b5655671b46b9d971bcea8f458 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/RectBold.png b/Runtime/Resources/Icon/RectBold.png new file mode 100644 index 0000000000000000000000000000000000000000..a0bf6f01ee50d172e044363a2edcd9ecbe47e5b9 GIT binary patch literal 2639 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/RectBold.png.meta b/Runtime/Resources/Icon/RectBold.png.meta new file mode 100644 index 0000000..8693466 --- /dev/null +++ b/Runtime/Resources/Icon/RectBold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: a25c9ab25f90433458e244e933003acf +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/RectCur.png b/Runtime/Resources/Icon/RectCur.png new file mode 100644 index 0000000000000000000000000000000000000000..556a97f556b5198416485e750389d505165328d6 GIT binary patch literal 2570 zcmaKudpuP68^_O#lg_wxX55O&o*6S*6k$`NtU1PIMnxk-mm#-StNaoKk?!)GaIaqo@AFUpY`771F7~1eD8;hQGu-kvcc!=OAs|9#GUn6$0fyr@!vT8iVO0WL zPe?1mlCdrVGJr|=62ohAIThkF&N)*`^gt@(UI+LJ;o zOTp}#Ct=*h4UE)G7-bj>8zL`5qMCgi>rO6&Ti?4r1>BwI4CYW`2aJy8s&=3Yju74q zy{5rpk{3Dn?$D6OboYGCl9B31jhXBr7JE-EfcoY5(<)hhL40 zU#{qP=)YrHwO_wYCS7>}l8w}Iy{(qhZpIP!I5W~1(zV%>zJoKg63nFIw5Fw1)tbQ3 z$}DGg6<6ost?=_9+HGS`Znk7d%i`j~VTQ8gVQsi9m>jH@D`++A^ghA1$Gqe{H9h8T> zeDJlE$4hVFS4;@cS#Q@NC9V&PTmGaE+bwTe);+~u#!F?dBPImBj4)~-1>QYgw@h$) z?WiC2WZd-k`nM+OF>&gD{2Q3cMu?Y0C%EYtHq3+YEulR=hhbFSD3eLn5a3xkZ(j*g z6Ky4`*})7fb%F4kS3`#N5R9uXpx+Mzfd&bxktsj`;1+IGOeerG9Gd!!#gi!oZyZhOBo0=@D7Gncwt(Z_ERf`^;I))@NCY;+R;Snb2NMy`>VGp=UxGv@EXjmE zMl!Zx(kA{FB0_0)5~zHs>>euosd3g8UR5!^)%sZD;?hJ*yhm7kZ)$B+A0un}{-3D4 z*KUpca|C5XuD<#2fa#+A#HvC;d;C%q(sMlPdGL*Ajuc^22cui9;LO;&4fggan7zPe zoxbwcf&ICRBijwjvl_|rEW0=2*jrTBlrM+l2BvAExVr{?^& zS?t!@Yj0E*;*F*%`t%7aSTmYR%Y&ToQjC-Hj}6a~HRy2k!JM&Q5kjdjVbLAMS>V_h z*ZmsF5uXU8afx?SBb~)>48?T;>!tYPBNuT*h6j=JU& z8XUDI=vcbVlZ;n1SJeeXMhH7;AvM;HgO%`Y|KB^MCIET zz>C|>lI$M6Je+FG57$4&5j!#scql1ayM$MuYtq2zjH%9O_7KxMJ@USUn0GL9;(dd8 zl2OlY>q{kTx%a!=8~r}(Zorwixc7qIksmU!)85%ZBP|SEJUXJY#mE_5ig*}Sii7WG zXFfIt=-{@;Rb2P;B&|{9QSU-^@K5WgdC+8`n+!`E_K93li&dNN9@II?vag-t2k<08 zzDKHj?K3dqLb$A^5(M4Um1i;1kMX~fg_JRbexH^ph53)9Ok7Lw{Vv1p=4V4W=`v#<(^{Kb{%lIrhS46$(G;CXinQh(9lW=sAK^J|eH^nqkYaP+rj;L@OpiZ@glkN#f1e?MEZ2(4AQ;uFZK(NFSbph@XPkp<| zGmz-hp51e?pbCqXlX4%>IuGtwJ~#=9>ip%u`GI7g{-Ep=fMmK3sjh31v2JsxL;)}h zzGxWbLn5aNbXEZPCzyS{`eOG40OJ zKEn<5tve;zkvAmVDR^MKpa?7DFpnV@$9k`zV%56fRQ#OnrUyj!dZ zS)S4Z4@_KY#C~h3xUO73_yP7Ss-IB1=ZiXrr2G-gAZzR)5fehMvnGD@*28s}vUFk@ zL@1+rSd0qaGV(BkgR-#}@jvaau*!PMlf7ylz0^a~3w70{q6660;noWpKD|SHb1Qr2 zRK*_Rh0O_*7FI^#e*3?!h>FLx;G9gr^_l|e1^LzK`1QE~{vTcPJJoA zRi6%0p581d+*f|!@L}M;a%AT<0a@-+kV#5e2j78TSlj;1#@=Qe_ZOE+&Sz4rn5`k*A4)L>=wS+BneA9!ZSsOxEAV zR_mXAnuuWOD_j#Mb0AUNo?Sh2K=&W1lh=2Gze59f57HpfLiCoYliEq>!gmn#^=nW2 hBMzA8RKJaKD$v`Iw-56;D?bAZ#B+9cDtGi3{TG&IQ=R|- literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/RectCur.png.meta b/Runtime/Resources/Icon/RectCur.png.meta new file mode 100644 index 0000000..97291e8 --- /dev/null +++ b/Runtime/Resources/Icon/RectCur.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: e3c21de628216474e86c67023a4bde1c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/RectCurLine.png b/Runtime/Resources/Icon/RectCurLine.png new file mode 100644 index 0000000000000000000000000000000000000000..7bb4076f48c5693ad58c2b5522fb38ccf5505a83 GIT binary patch literal 2987 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/add-bold.png.meta b/Runtime/Resources/Icon/add-bold.png.meta new file mode 100644 index 0000000..046271a --- /dev/null +++ b/Runtime/Resources/Icon/add-bold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 1d381dc371569114399174f217d02e57 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-double-left.png b/Runtime/Resources/Icon/arrow-double-left.png new file mode 100644 index 0000000000000000000000000000000000000000..1d88b57c3853b7106f5d49e0ee993142a78d340a GIT binary patch literal 4692 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 CYCRbM literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-double-left.png.meta b/Runtime/Resources/Icon/arrow-double-left.png.meta new file mode 100644 index 0000000..140c1ef --- /dev/null +++ b/Runtime/Resources/Icon/arrow-double-left.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 05a970b527718854085e371889ed1337 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-double-right.png b/Runtime/Resources/Icon/arrow-double-right.png new file mode 100644 index 0000000000000000000000000000000000000000..5f1beda8a9987ec75f3f9b00c89a9e5b86d857e0 GIT binary patch literal 4665 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-double-right.png.meta b/Runtime/Resources/Icon/arrow-double-right.png.meta new file mode 100644 index 0000000..d04834e --- /dev/null +++ b/Runtime/Resources/Icon/arrow-double-right.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: e03ba78232beded4791e0471c383898e +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-down-bold.png b/Runtime/Resources/Icon/arrow-down-bold.png new file mode 100644 index 0000000000000000000000000000000000000000..99b07085d7b7a18e4828065e061f0ea0de6539b0 GIT binary patch literal 2526 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 Pum@OM+R@6*f9L!cbr#)k literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-left-bold.png.meta b/Runtime/Resources/Icon/arrow-left-bold.png.meta new file mode 100644 index 0000000..6bb7186 --- /dev/null +++ b/Runtime/Resources/Icon/arrow-left-bold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: ab609e22c3f1fb644a9a80d06b4ea35b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-left-filling.png b/Runtime/Resources/Icon/arrow-left-filling.png new file mode 100644 index 0000000000000000000000000000000000000000..aea2b29b8d9fea3ffe479854d8d19b65ccfb5740 GIT binary patch literal 2876 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 F{{c}(;?)2E literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-left-filling.png.meta b/Runtime/Resources/Icon/arrow-left-filling.png.meta new file mode 100644 index 0000000..f5fd2d9 --- /dev/null +++ b/Runtime/Resources/Icon/arrow-left-filling.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 2c71b5242ff0789449927a53a82e2a8b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-right-bold.png b/Runtime/Resources/Icon/arrow-right-bold.png new file mode 100644 index 0000000000000000000000000000000000000000..efa00b1490adfb50c84ff48046bea67e7450b786 GIT binary patch literal 3353 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-right-bold.png.meta b/Runtime/Resources/Icon/arrow-right-bold.png.meta new file mode 100644 index 0000000..42c0df1 --- /dev/null +++ b/Runtime/Resources/Icon/arrow-right-bold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 40ea894c38ff6ad40bd401f8b9f54d32 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/arrow-right-filling.png b/Runtime/Resources/Icon/arrow-right-filling.png new file mode 100644 index 0000000000000000000000000000000000000000..be2cf3c92d8095ec057e582cc677aa30a389a24c GIT binary patch literal 2711 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

xn!a&GR#k$=WAX&fD15 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 aFB*V<`j-pwOMeNW{s5SlpJ%Om6#j2kSh~jm literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/arrow-up-filling.png.meta b/Runtime/Resources/Icon/arrow-up-filling.png.meta new file mode 100644 index 0000000..883f344 --- /dev/null +++ b/Runtime/Resources/Icon/arrow-up-filling.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 0615b2a2df521e54aaacd16cd30a034c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/browse.png b/Runtime/Resources/Icon/browse.png new file mode 100644 index 0000000000000000000000000000000000000000..24d5e79d5a15d6745d2a893115c2231c2011b3de GIT binary patch literal 6017 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 y4g-u~4;+sX&T{fA1<+L2Q>#_6Cj1YL{MxPn literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/browse.png.meta b/Runtime/Resources/Icon/browse.png.meta new file mode 100644 index 0000000..68d9e5e --- /dev/null +++ b/Runtime/Resources/Icon/browse.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: fc16eed0230cdb94aa0fb47f42281ed8 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/close-bold.png b/Runtime/Resources/Icon/close-bold.png new file mode 100644 index 0000000000000000000000000000000000000000..7606d07e4c4ed55c671798989ff593e49adbb8e1 GIT binary patch literal 2946 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 mpnv|XrDVUw7HYo``ZwIU+(+VH} literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/close-bold.png.meta b/Runtime/Resources/Icon/close-bold.png.meta new file mode 100644 index 0000000..078dd07 --- /dev/null +++ b/Runtime/Resources/Icon/close-bold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: cbade0bcfd572a94298ddc62f6bada7a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/direction-down.png b/Runtime/Resources/Icon/direction-down.png new file mode 100644 index 0000000000000000000000000000000000000000..51e46bf55b941da615ae4698e990ef873cc57e37 GIT binary patch literal 3429 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

11tHAq~gCYM$+4CW!bAFjrAc{`z*$XBe){c-np# z$rG=n?-dCyQPFXH{m&LU|FuD6Q|S;dErQZCCDaBFv~uSM9r2C|#6h$iYhT<;?s6dP z(ZK%V*x88XK()JS-DU~A>EGL*6f`)SuxuUeVCP;{NJj!0gp0M^KGqTp=qTH?$Z;r$ zO!}e~otp-jD2czwTS;_2QBfCADef$vPL_$l$Lh9|ccoPzC>z7b?uQ9&x(U2i)i9II z`@$WRo3*u+)Q_E5VuoX4 zp9idw^rH-&SuRwMJtvO*N#{9s(k@J@4Cpv8TE{R?Jk_xYIHJ|Aw-qn^YcrdLhr6@3 z^_yPGS=kAI5{Ec`$0?l~#uY(~BV|z-O#C$=kdr!)5cZ@&A<=V`50c!Yl1Q5wc{jMH zL|!Ac3&@%1Oiy3++-Xie&IZUMw(b}ly9w{Tt%Rdl1`v-|CpmKHz(e`KAyf154Bc9~ zuF<7HgGiWD$xwedEPkfH&NG_=!V(-wx4n53SHa?W;^aN@`q#ItL$wfc#$^srUto|v z;!E^o0j~MZ20&TtG9*r-y~X~de$8IZmx%^S@oxqA$O(~Cyc~nKlmV+yvk63apHn`-`hA0>nD2|P>#+^}8<0ECfY8rEB ziGVe=2rKcqQ82v!pwjJo@N)n97;5+QK%I{Ct3a+(TxS)qCymnm}{AV{!`41-Vs#RST~lw&$`e_TT}-KBI$ z<)W5vx;(Eb&IHDr-hrN6V`_7GWL&$I7#v$p^SInaICTayShX!_Zddd9YrE%x%4v+Z z%;kgAK=ZJ)R1kfFd*Fe5bF;u*6B_tdDdUS^ZBNnue+ zYtNxX)fY8$Hvn#YUJP~>cp#GVZS$yN)*ULc4K|^zu(Gr^#w;DQ$%G0=@#|*cvfXY^!8lc%{nqMd1Hp-~+6g zgy=Z&nUfW1VaIRY4rHJ95FM{nL3+FaQ>|}v4S4~Fw!$|<4DxcQROpR`-5RnG?eM$o z!F4k9X%u7ikrkPGP4KjQ3Ymgt7sBf720dPSQu&!mEa)Ev0Pyc$E}T<|EVje`ds)E% YUs`LHH)G`oWD7u7%Sf|Y-7)I_0SCt5sQ>@~ literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/eye-close.png.meta b/Runtime/Resources/Icon/eye-close.png.meta new file mode 100644 index 0000000..e4175d4 --- /dev/null +++ b/Runtime/Resources/Icon/eye-close.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: b64e9d2ece5c62f4dbe2bd772303abe9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/fabulous.png b/Runtime/Resources/Icon/fabulous.png new file mode 100644 index 0000000000000000000000000000000000000000..3f9cbb709600d81ff7b68b19696e819e53f3b391 GIT binary patch literal 4627 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/favorite.png.meta b/Runtime/Resources/Icon/favorite.png.meta new file mode 100644 index 0000000..1746db5 --- /dev/null +++ b/Runtime/Resources/Icon/favorite.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 3423251e80fdb544283e7d98c6502361 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/folder-close.png b/Runtime/Resources/Icon/folder-close.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d4d468f9170babb77b6ce96c343e61c907de5a GIT binary patch literal 2569 zcmZ`*c{r478-HiKrWuqmWsSVUjFQMwXc1$bAwu@0oI(vcmUK?m#%mjljFCNSC<;YH z=48n>b!0y+94XaF2MGy>&w1+m_xt{M-}}0s-*sR2@;vwNzLRWiEVhcsi9isvl|eVP z2eSA3hsS|`wM$JQ1Q8xFOlc06F*5}zu49%&bLR&|Evs~dzJl}s}sT3ZWI{C?1Yh^EC{`B_E;PtVo^;|WL_8puVS|uq7bUN1!ysG zXr$I&L)1taCYGfeOZduS@%9NvZs;0AG&$8BKV^|wh)~>i0w+*hAbB1H!Y^-s zPFyj9Xvd&8?sz_xg#8!B zD2clq^Myk-gQ{~#!VD*)#vo^H8h0vi^()kFxgc%fewrfD`EKPWnFCq@J-+Z#pbu+_XkWMX|^;mNm&h zI(<(>#2 zTKm}8nZ9q$wKv#=3ttnzqeLj3uCWp*+0}XQl;{svZ#cgg{%X^|W_xO3v`m^-m9GJU zG;p)fXsCKx*})23E&pqeK%<#RxiEUR==?=I)36l)>_>iQ4klK~y=Pm7hI}QV{xwRc{{OEra4mN|5Y&fLj}_ zbx*^;O$a${p(3bnIO4zN(c*14oN}6%Ace|w9=+Uj%9l*vb47cE zbN6&5$01zV1%A3UdB!g z9Bjg{Fd!LmciomVM${Ft__vSKPaOvBTOgHnKN4J?xJCH%H3$uLa~WCyjB!lf;*-xU z4+-yEAsI8Lu=u)cnI3U7h?cNdPcR3}B9emFZDC8%QbWbcR;FqGIho&!WOGvIuCUwJ z#`&32riy80@lfod*rRy#!4SIRctHs{C>KIKd^;tC0OQ1@ocNSYKze=5Q20;LCC*TrFA*&ENiWf>4nsXh3W4BUv<#u4h`+C*Z8(2unrrdzSa zKb#m>x-)4@Tap7Sn5FGrm$v1|tnHr(aQ0PGr{~_0mBj7@p3H6KbyZ?IP!6Hs&KSsW zb|jfD3P=b$AX}#Z*vh7weILTWs!#!Wj66^aa28!ng@s5;Ck{LT5icA>&}6`oU0vk+ z3!t!(TiL~Duy~T4yp3!kgmO;$Yo{VCL}EbSM;?pUwy72z02FO%G<7W)P@HFjN*?GD zx{omV`or(~SLFZb4C!qKJs>U_zw0_;pa2yk6gd-!cIH;_GfkN2onxPI9vml?bRYUm z2whh|Lp?m~kJv!8t2$+gS3!I|UDSptnD|cLB>ZCzxKNMrUG{1PLg=*`Xv*0dqE%^# zw`YR9%GKId96?@HPfdc#f!(x-X0$3czuSy!ouhx=0i@vfUJ$Vz1HX(*iZusUe#5Dd zuLtP;zi=?hu}tA7DzlKsnb+gKfq)I}>;Iw;%sd2)%i_SyB;{SY)~kTS(f8!0AyCLrVVPDxtTMs=5*c4(1BTZ*Pj(dvNCp{y%rVr;xez@Q4W(XB(H;4#&zgJkxfbuELz1y52KQMuj_p1p@1gL-o!c{N*Q8Sj zotvXh9^Ues@M?qJ&Z~(motVv||6G*ptl213*LRMsHq^xjQ0D_K-u+~?kGkNs*@RP&Z4GY`^sU`iCo#k;4Rb&HmG9qG}!1^H%ND#^-E!tBHAu?;`V zYf{E1#P}hD@0;^>xlrS+OH>6JUxGelIdZ89%)HRq6|abf*H-0GebISd^M&ny9| zueTqJ|EKwba^RVyN^*i0L1TZ1eX(ly$h{&v=Fm2r4Q+Nte>dI=~Gt zng_6fxx+r=?=VU0=GZa(x;O-FZjM?HiGhU<_kEKSg3@s%gs|Z<32*{2%xp|+jJ?o* E0o^=7SpWb4 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/folder-close.png.meta b/Runtime/Resources/Icon/folder-close.png.meta new file mode 100644 index 0000000..5bb22a7 --- /dev/null +++ b/Runtime/Resources/Icon/folder-close.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 767bad70ecc44924681a0cb2ff0ff1a5 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/folder-filling.png b/Runtime/Resources/Icon/folder-filling.png new file mode 100644 index 0000000000000000000000000000000000000000..47c671a46a8537f747887819a6164d011854abd7 GIT binary patch literal 2436 zcmZ`*X;c$g7Ouns3J@s*2pEZKZJ|^li?ASiL{1si>Dy_6) zoaiX6Xdx25te-r2gszo_Fx6T9}#X=3~@OJq2YjQHr;2fwirVnz-lu1JYKN4kGW@}e|t zG9n;}S1yh9fbh=9cP!W*!IWy~b#W_;1hcBVKkPr~$%k;t;-k_&J&rWtXA{O!5=?Ae z=M=pe!k^VfREPnS9S@&N?k%fFFut8)rcaFokCx}sy>lRuist%DFv^kIckn6yhFfOX zN#@ry1)Iz*E*y5Ok7;=Dtkf>y)%1aPS5-Sdx~tV@RkY6x%x*qA*Yj++I#RH6-^zk? zL7`eeUTWX>d(F_@v`o#i72}D+mF=qcBgaQNyp2fQTG6f-=e5HG$!qydH)m~+RQ?ft z!aB$)uiyh4Ewngiqa+pu`>$R5jB-uT+pSH=z3#p9a=@j-iF1}3vQM{`GE@WUH6s(*Il8VF`jVSVrjHa56 zMuvoHA?IIeFM8v(?2N^bxHv7d>3|l+;f^wnSTspXTOr2gjU-0n^Sv)4U__wr{>W-( z406{9zqAIV|917P_vwji!e)tI@pSf=Vl4&j`f-oJH+AGKRD8&QxAMvFHBEKHaS>-e zWUFS13!Cm{dq2uv%J=Q(rAJb_7HO>R`puv^a;`dLpnxgkJunMO#`xMDxq}2p!Ovgr z>H|;NnjNmEp9$%xN!c*UlNMy`_?ZdUed>1#adD{7%65W5bNgVR}-! ze@eIp9%fjy`?3DH>1Aw0aUCl$IVPMxHjXJJyw^f5-2cLjE9LfBsc2;RMIKs_$c&F$6Hn?pIMMczy|1C(strHWV15T4BJcCCWZibpqP1-c}7 zYLMx(()m?3=KMo(_(=*!TG_@-u_hyH{H_uS;J3hUbB86gu5|3;mRhSQU7|QF_41R{ zt<>b4lPMAS_Mbv5JR1(x&lc1%W;Jp?pK#Z0k7hSm!wRi(&EGAxxEGmjlmYjd4ZB*J zDo!7T*JzcmkQdOYzQ+zU5wxkmXf=lCMtm=M)P@R&)UTow^_`vPNP(x(cL{itHJNC$6k>5v&fz3v+?EfK#s4LBKnK%q`GSPJhTuu`ZGe5G#C||abo#%N6qU$f&wR_yx-2fVONrZaXk`YRcIafay62U8w!8RIzf5rcTRoar$Uf{<; z#oT2K{J2#U@jAed^U?iBfS+6WMclvOH=bm()C|nb=%lIvNb1DG(X#JBjm5>*&^vZD2mGH}7S(`f&57!ChTYt@(SaQ1DU zl;&o-&V~e=)K8BDb0Iu?Rb1COV4&(ak*G(488_STvy`n?%5!zfLrlPMTRV}Y2^c!% zd@orB82(TqJ_#;hS;tIZk_TZEIhk%+1c}7V&!gc*w`-KU(W0L#cZ*^DRv7)4Xz@w*mhtQlPzuZE?`JQ21tB zy#9zEN@t~oHd0BwthaW`vFNP_%}zBJP-1^fe=hpexQ?195w1;3b)4UC*!DOjW^qZ) zjmH*LkoV@NrVCrzd}tx#7QNVoZ`LFEXp#+Ucl&C2BC3%CTV<{aoTsA%OWEyS#L^x3~_@>qP)+{)$Ycb9y-P{-=pK@j;igIB`X{gDe0|S zGC2M$gE)VDoscMJXDvz8CzTyQyS5#?bk%>NbG1fbFsc)t)g1I_0QQE(4H?4ixv7e| zibjKm8Q=J5ef%}wwmQ(D@N3wy8ix0?!7lv;7q``I3V4*^@AJf<=zO#BH~oL?wo#hi zTIU|bPN{8q_Tan9!<{Kbq3^8Ht0Ie3@jH4HdrF<|&t5~iZQ1IgRu?eM^{}cBAVnYL zE1&KIl13#74n~pp-Eo#SG@IC&fZ4lc)y;jbVD?T&)P;lD8+Ze0v?IZhcZ(m=^si8; zqHSq6ozYX8n9@z;2+Khgtnf-g$ZJV(A#q94Y6u3tnwBmey8NZ3vfttm63>J1CL62v zBz+DVQ$Evc2&C|Q34IuFXwK>m34kd`w_%KoZ~+U5VQy?6kih@R}4ri#q_)V1;2C8UCzDl`~JM2&pqcp&nwp4%!msif`C9ETttF_1$gfHx3jT; z$1*!wHUz@+ifC}w>JIc<=A+t$4t~ROCkY;jD>3kxYYz!zaKj{>_bH#Ce8ctm2kzRs zr+8mGYAi9^-FWhC{crQ?b0ihUgW|>T6b`e?ZtY0}V~B#*$1vD6BXm|G3})a1Ail|% z^HQ@e1{xzc^T-fkEXlt*9tJCj-zW~`Wrp`)ViTYFAbBx90k&n4GD^~z$Qaz|Zd-s% zd4A5&+#CwEFu#P-X#p({(Q*+H2sUcO*W#6Jux8MiF&feihXE$MR+^EoEdlZjd!D0g zg$+P19Ulsn^?N~vGzb{Hv2ZRUL+ndPd(FmTpr4BK2;E!1Uxgz7|dooY2T|8 zjE2$aT)_M$m}PHwcrpn*o6SOyjDM+}#Ikf6VE+U;t_PB13IM(*@$U5^GQ?B5Sx)pU zm`6W_epMALrn+jq%?1|ZtHO#oS!2G1K>4K$5g8;+8R-D)srfb3)NB2e##ywPHZ$~RaqY$pt#wr)2o3*)o~Xc z{U`c2t71_~8ba#+QwYnFFXxF?a4P-!2dhUCj*sJ>BjV5G@|=`@Yx8Vs&a%7nn$$jZZo~-xag{&eNZV*5y6HM z#`g2vT1rE<2Rv$!+jM2ZQRn7(R{iswKa=RM#+xlWp+wXY&1mK30mXodJ!>?B)$kW< zIrPrjcvAbd92UdeVX07b@p^Nm0@Y)F!@6QVjGY_*c*gAUZ7YRDIWn(l1UB_9X87HS zv#ctSOfqE2kKTfdogYtlpDcmu7oMe>Cu7b=LHp<=Hl3gI+2mZkBR2h<5+b*EtLPB>_xi##9t|bRF@9vM8(X`D1%K4>+zIPj7=F<+~=ALfYI<@L6ul>t(BWe7F@5%VTygoOHD zw)OCl)cxqX+mx+^?`#-xl#W~no=^86Hp7=Rpt3mP5g`&aFy~woBci^Q;%R-%Z_eZv z;0IGR&l9)D3-A+ARRA+5Wrf7FNzVUOgD?_YXW>O79zKvz&&biQ5JpozB;>P*-ezSr z3uI>FmLmAKBg#fFx<%?Pu|1>YclzLbt@mxw1;C_ub1b1dghv?zpz?3oaXRLaWw3{i?Bi(o-gR$e5I6Hbb&A?rJ%~M z0~?1ww;(JkI2y_kCo=c*;MDT;Yg2JgSd0`7!QGNbPIw@M6-`w^$`>gE&J^-`LLLk9 zQQX}ZYoXKck5UX&3B^ni`|Aykk$H0tnQannSF9br>`OW5^}T$f^nUrU^% z)ei=Po9+2^4$e<7xObf5ZBtX3(#F%8@(dWYsmVV3)8LVXmjIy{=EBYywV-e$33c1C z*s*~meXyE_z~iYW^*Y#;K8c95=JH)t^VkSp%kjM3e9EUG)xQGAcKh%%0t(N8uy8tk z^GiAwKDX1nxFF3_r-#LkLn;kM{5*gL`+WCCzHnJ8Hf`8@JL)ulRi;-ZGeksASwIUf z+2t_=xmMLw?Z}y~Cy#(e;cIkJKXhO70vG?8M3%_fpyRWfoSVLs_4v1|w*wYv>Xe`S zq&T13j+@CFJjC^8Sh50FmYC(g-=M~~`o*ZwXmWuJrfkm6J?>f>dvdKz@TTWXpJrsj zly%191OLY80wIh}Yvl(tyRt*1e#AuT%25l9!f%o#_?-z;c)Q)v>%G<9;DCsaG=GZ>=`VXsP=gC6oyYEYlg0@X!{{Ib4QcQPKD{jBl=LJE9z@=x;O@oTQcoH`h<^#J@=rAPyF2gSxAN&F92R(oomiG{?~ZJ>-%g`ryhM@xSUp*`*Jqea&9TiCf29^< zyYVWx?8We5*k(?x^S#R`CQ(W>M2NSHOGnT_YWBc(7#~JcQq?;2j*xk51Fpz4Ic8wQ$ENWon#{0Et{beERvb zFoMn1LCJ(G&1_CSdLy2$Eae;;PJ0%%`MdpVI5KG9lP<-DRc=3kg*7g|0q!`EraMyS zq2-+wJm{`5|6LBvORoU@(k{GI#v_*ouM4XyeYoX0xRmZcbY_P)>AcfAQroh(zCMm` zw65Rd92v%27kY(5dp;Wpg)1tia@_J|U{lVB|8%dT4zBX`kJ@SL1icpYxDgdn!+5`O zeCg>|`L!F$KwOC93%|kS_EDMhh70MBLN%i-7(pZ&WRehRs;YD<*(dbS)Gf^`2O{op}`ioB0@hub2I`1c1YtVv{Ktd+6 zB@y0J=c{-@wX}Ort?T>L!s8H8kvp2v8c(!vMDmPO#~a3LTv4jyU`9+}42CF-#1Lb~ zyQ&#mJ7XnseND9_E+7Ow*$cmvV+H*|vbwN2c}sHVK=8Mbk(iX|aty?NQO>kndz~j_ z&6?=gyi4&_^d7SA=*~P7@Dy?eJGx=m49@ilNuqaZxrtr~D%N1iq?$q4mJhd_Pj?Hh zu`AAU!vVgQtH0o%M-P0x72YoYqI6XY;>3uHB5(q~cA4JD?7Zm>C≫YgU)Cb0(%x z7m0$8e@AaO#{axoJ|RBAf0z6zUZ6#L6gN0l_%tA!MBiEGh5jgOXwfdL$etWsX&JZM z&sx5@x%yEY)*b>w?TzpH9V$`K9VY(jlhXP3reJ^`uI^lZ|1uc0Reea&9R@HR=VGVcX ztfr3XgRVe)E3oBVGX|5HDjTPH<=JooJm4a&vV4`>Pq3PRKgG^Kp#&zq+w_a0b z6>AO-uDlJTbeyrK@SWks@W~ZZJV9g_EwZ_92qJ=nm6h-)xOy)#ErUG*A;;k5L6N?j zHMri-u#h8=HVr*e@32ncylYsl_X z*!mEJs&mK_iLgiD1xepQwgU8@gXw3tVnDfC?#`j-=5iAQW+YG>Tc_`~(OArW) zj9)~=f=Fb5EB;Yr;S2$aMjYBWLAnx_zv5p{138yATkHd(T8}e((wjiA+kv|_K9RD1 zz5vVll$o+}GUQcxgBO|rj>5c(?iB`hF>Jtk0RV&DPn5D<2WufbX)ep=b9x}M|NU!b hPGf)-I-}-vH zsc)igf*?rUoy|H3zRLFx@(FlX1XdP9kXooa3pp?SVZ-JE%7 zaP7`9!ycGk;E9?y65)F2i4Q_)F<`2QV)c#naf%YkR4RlylQ^>D2&GhAcr#(22rYuk z+8tn|C#}JcVcc~SEo$aQtphnF&G{!OggKl&+KGtBnbGEHcplP6t)ykdybT z3Lq>~u(C~T4no5hst|o(uvSko{S8DZhURs}UlL)b8+T_$66Sy>XR_DRHz2NIjCnj; zgc_vX7*B$cgIV`;oXwb04Ht&hpu7U1MEt$xtpN^UX%`OY@U&2+f5Z#}Cg5bP>$Ex( zrnEwtSu;1DOo>?7eDz%$jGU5X9Wgg&N}ui&z1<1oNA5XSbOpj_O_0@#xzSjBNt#WS zYoTo0C5>4zge9@vB^>~SUXt1}Q-tm=Eq%-*ZISy(!|BKEObGbJ0!48KIBuQmK)nQE zuAW?kd@KLV&!DazuRHN=JaY(6Gwyw zXvsdeCE)E>8edj{f4D4KyQc`X>J=s=5@C|jB_dB7WqYi4YWRrIQPYEA9z{I=;`tDHcw*5#=*JeXFV(3KYdxf_vmkyi--Tlt8`H(FvUG?Ki`=%ZU9(shHV~^$AeSwr4 zR}Wv-Z2Q@L?1lYiPRDHvw#90RU0zggx|&!{vJ~>`U3Tqk5B2quMyyrcRt5+XDc_|x z-+9t-=9%H00e>ws``SNG%+E|KJXtCm@q;Yk^g=nY3Fx2yewxZRtfDAsz7yWhwa3$A z$y-h0ikX)gTo377=kk;laS&Dv-wwtj{HiJ9Jop%C;Y1s)Xt$ZzLa4|?Qv$M-;_EuM zPTd5CCsTr7s+`{&GEhxT%_G{4y8sY6rs^0gvba0Yx?6$N9FkI3t&$ske)y(&H~ez% z%d&sG4SZBrh-T>~m+VOaYNm#GV%Abf=O6DcRN!WdO{MeB+*KP%+2B(5$n{s#a9+d0 zsd|RMay_KVpUK5ELd$4-Z_GPO(wAn>Qupe8NM`ZQ56S%66C|^jB^}%0w^-7<(w5k) z8cqHF^;kpCtGP+MHhM^glz+yQ-Y;#JXG2(cYh_^=D7htNq6Lg(;MrTWPeI8yvz+dN zlKUGfNUkFEd*WcFKI1h7Woy{S+yH$gxYi%7LBPj-??q9Gu#9$0GWlQQnopSO1gz&6 zQt>lr+GX3?ADTg<#C;dsPy%7I{OEKsi4viTbBnHs*MtQ{qD2=Sls%QWWuOqkg3VQb zb^$2PhOPH$Old#Ku}N3VeXSj#OUsD@h=XzlmMw^I(D|pOLBLD@_?t%=5axf>M&M68 zi??$;MtT9qoVgUUvK0{K`Mmm%M8sTqh7|2+N5E4v-A+~jDvT#~YI=##e~ujpi2>8~ z0Z@Uvv(d9g6fl|EsW#zYGB1V1+E@UZDJ*Zy0Jqn= z=%5Ei%+<^P$bLXGh^zb}nDOo$?h!xWIb&qB8_aFezUdgC^_pjV;Y`3;KI~*KfMWYv zT{`0(P=xR!9=Cvmom>fS@)n^3dNONk0`95b*k1+sFy1w4*gLF17uKV3B(Y%Rc)s^v ze-4g&=28a%&DLE#_UtHNvI06^Q2JE~e7o(1pa>8kU%dUn4g%gcF;m+Kklky}<>f=z z^g=>#1d)HtRH;WlY#fI&r5i{k$Q4jsTi=KKRDe;{wD)K^0#e~$O+4Q$W^Ttc{`j30 zMhEm_;oXr7O4P%e*|i1mx$ey<4ydEx+S_dzOf{U`%Cr6daryA%*3x^`xjfJn)3(BJ zH?X7*1ac}s&%BW_?wS(eU+9)){a_bJ3SK0TiR%OiT3@$+?&J;j?I-US?FNV=S*+@L R888Mkko$gbR^`4h{4cS$DboM| literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/minus-bold.png.meta b/Runtime/Resources/Icon/minus-bold.png.meta new file mode 100644 index 0000000..ce99d22 --- /dev/null +++ b/Runtime/Resources/Icon/minus-bold.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 5d082959ce575d240bb665b1ec42d749 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/more.png b/Runtime/Resources/Icon/more.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba682b476d941652e0955c7dc5560a5e7f6b491 GIT binary patch literal 6112 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 CH<1_s literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/notification-filling.png.meta b/Runtime/Resources/Icon/notification-filling.png.meta new file mode 100644 index 0000000..bed46e9 --- /dev/null +++ b/Runtime/Resources/Icon/notification-filling.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: d0a394a5baba04a438ea0dfd9d43e33c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/pdf.png b/Runtime/Resources/Icon/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..2f4a802f5d1c301f858915de9a73c7ea3759f097 GIT binary patch literal 3892 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/picture-filling.png.meta b/Runtime/Resources/Icon/picture-filling.png.meta new file mode 100644 index 0000000..fe2551a --- /dev/null +++ b/Runtime/Resources/Icon/picture-filling.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: a3bba019c17cf304fba3a18cf9622377 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/picture.png b/Runtime/Resources/Icon/picture.png new file mode 100644 index 0000000000000000000000000000000000000000..097a5514782a0f8b7f1fb01c6f082ce3ac57a84c GIT binary patch literal 3987 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 ZMgU>X20JL^mj(g}U}a`&`oP3H;eQ~U^F#mu literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/picture.png.meta b/Runtime/Resources/Icon/picture.png.meta new file mode 100644 index 0000000..7a06f4a --- /dev/null +++ b/Runtime/Resources/Icon/picture.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: dc9bb3ecc8007554abef050e16c627e3 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/save.png b/Runtime/Resources/Icon/save.png new file mode 100644 index 0000000000000000000000000000000000000000..9ae19051652727d7ee12a9be5530c0d30daf4600 GIT binary patch literal 2616 zcmZuzc|4R`A0N{*?lYnuTc}(Q#@J>kA)(Af*Pv0zu9TO^axJf{(agh@?bR3|Yn!5B zvSn|Xij4HO>006vS+f*Lk;!}R`}h6hdCu}Xzu)(Ke!uVeo|AUe-dajxy95r0ld@r0 zIf7RB_XmrEKi}<6DGo=Rx3QuhixHbE5p(=tf*e{bP2Sffuo1V%7egzg8cp{8v?dD^ zqg^|=ZfESMUkABQ$+b5Z`r>MH__jY=-&?@6_nvroiMqX%CFmevH4VBLACu9KE{xK7n8W#I}ATX(ShnJn-JxCP=rknc%gMl9Jv z^>YEui9Q+oz-uixWai6G_M?d@0yp6y4<)mcn``Gwz7D*9s3#g|T+YaVB~Li-n0u6uMYX0Jkf`zvQlAGBj&BG8!S z+tTD=D>uJ)bJkn=rJ*j-_obVp_*>ZI`Z7-YailG7I_IKX{s_6- zpttOa-I!kLpZXJ>?7Zk+uPDcGvcXi!S9KXQFN3Er7!Nm6!nj61|Hu!8dc)36r?QqLA=KrBOoYPG7ArU%^E8mS&W=+1=fes(L zpE_;JJt1?1&Xc=n^AVwOx4wXv9gU)fA-R)~w06H8y zgA+&HK_@0?cd(Tb=+t!YK7F!;F($s1K6G?aNh^m0bN!FEoi02i1BD+qZV~TBiP*SD z&%!{K9uaHSFF3a;i_*10=yLToh(dSqNbp#ViQ-t2AeD;>twUD((gXoqG_~jP$;%_q zZ(N9;*q6KC8h?Y~=!{tZl>AeSElr|xwrulwsk#CJF5g=-O}p=JgOueQ>W#mA-xfpf zSKrGT`{clNfCeH8rZvZZpNw5-xv<(O8U~IZ0%vBAeT~=LA1w+lO!PkLJ?XL}^6S** zn_SnlL3|ILGP5}5!-(p7v!4DYg9N`><>|xT*QW#B!g!ywUvRbbM{psA&~BJmH0)~h zFg>;H2B~PBOvWl~fB8{!BxRMVpl`EGPmgXu+KU1#K7>TrITk+1AuU*JF~ zq(^arC*~3>oE~TPw>A6BeUv_vfoAjQlu;OEG;_a0?OF#Qr2T^W4M|%ZU7SievkEjq zB`ecFOAJ!FTT?b+iKCx0IlW!O`3}tG4UyXNa9EYb)5Y7spBIrc=K&NTPqDLHjfLKR zro4Y3W>J-KuMlxS@vuyVVzSId9M)b*lqLW>JDskz2;6}A^)^VN(~5I2tT+z{#dyUY z_iZw?e;f+5v2m21hxCJetk6bP;W-!;W4;xzS7)J%fd{SFaHcK;6(+yY7lUS+qO4A^ao91|b2$;% zLSm5hxIAzt2oJvoX6*{)@^JtZ8_2vxfRHlW_J11I1~LsF0jC-av8Mpc%3rNyGePcw z?A=ZD7Y&%s?`69VI-kEg?^k2wX;gd{a~rMBT(0~YKl`ppDnu34ej7R$dOs#0sBP85 zmVS7P{Kh`lmW+CN-Q#`@)L+Z`WSt~@I$G=P%}Q9~G3KJr;aSgh;HG1N2VZgJ@!G*fz=Tw`GArXwlnCWZXBFEm-`)E?htHjr^a zhepE}(1xdkMhSbaiaM6AE2Av*m-mz-`Wpsho3RGOy&;Eu;b8(B`s5D9-|72pXZB?rM$$WQTLt8#Qz_ia z6Q55l|0XxiN3*>NQ(?Axc#F+%sO}l@<%@E@pCIdB?-fq1hL`$>R7Pe@JzFb`a^Lfy z@ZUJ439!r)E%)yu5jSNHjqD!qGK_-i`{ zCb0kmJF6?ZrayT96*2SQOFzn`3NIX>u%oppZ6%5xb}LgW{B*R! zH}%4&6%=-Te@1g9QsqNbg3AjP8;wUio$15=>&q84s&q5fGUj{AiQ?W$)6W*qbi5r2 z(P4-p3#9S0a162TcbBXbnkFc6&OzzZI{?vA%<7{6B}}#DPBntVAJV)hNE0$e%4E68 z`$&K?bycK^Lpb_9hQSLC;H$8Xdr3hIT2$*Yv6K-~g&YkH?IU=2%qFKc1xU`5+)K&k zL^M^C`E+|04wH0slvyIN&{9K-|8y)ULO(a*a2p%Or*+WN&rn;7RO%%1C>y literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/save.png.meta b/Runtime/Resources/Icon/save.png.meta new file mode 100644 index 0000000..29d3f33 --- /dev/null +++ b/Runtime/Resources/Icon/save.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 7749e16d34aa2f6419c6dc9b6a385ea3 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/select-bold.png b/Runtime/Resources/Icon/select-bold.png new file mode 100644 index 0000000000000000000000000000000000000000..106c77a2103255e33a6af19d182869a7bddd12cf GIT binary patch literal 2733 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 GBmVRyC+qty!Z+tXLJRt*xlqYS-QsRH;2` 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

4ib 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 literal 0 HcmV?d00001 diff --git a/Runtime/Resources/Icon/success-filling.png.meta b/Runtime/Resources/Icon/success-filling.png.meta new file mode 100644 index 0000000..0817b0b --- /dev/null +++ b/Runtime/Resources/Icon/success-filling.png.meta @@ -0,0 +1,170 @@ +fileFormatVersion: 2 +guid: 3aa912dd431a22f4681969c5a18bf678 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 2 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Windows Store Apps + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 8192 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resources/Icon/warning-filling.png b/Runtime/Resources/Icon/warning-filling.png new file mode 100644 index 0000000000000000000000000000000000000000..d81ad9a2477c96c7baf502b3e4b00651e9fae80f GIT binary patch literal 3704 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 Date: Sun, 26 Jul 2026 16:49:32 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=A0=8F=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=AA=97=E5=8F=A3=E7=9A=84=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/Form/UIFormControllerEditor.cs | 68 +- Editor/Form/UITaskBarEditor.cs | 40 + Editor/Form/UITaskBarEditor.cs.meta | 11 + Editor/Form/UITitleBarEditor.cs | 14 +- Runtime/Form/Controllers/UIFormController.cs | 343 ++--- Runtime/Form/Controllers/UITaskBar.cs | 91 ++ Runtime/Form/Controllers/UITaskBar.cs.meta | 11 + Runtime/Form/Controllers/UITaskBarItem.cs | 108 ++ .../Form/Controllers/UITaskBarItem.cs.meta | 11 + Runtime/Form/Controllers/UITitleBar.cs | 42 +- Runtime/Form/UIFormManager.cs | 414 +++--- Runtime/Form/WindowEntry.cs | 102 ++ Runtime/Form/WindowEntry.cs.meta | 11 + Runtime/Prefab.meta | 8 + Runtime/Prefab/Form.meta | 8 + Runtime/Prefab/Form/Window Title.prefab | 1116 +++++++++++++++++ Runtime/Prefab/Form/Window Title.prefab.meta | 7 + 17 files changed, 1905 insertions(+), 500 deletions(-) create mode 100644 Editor/Form/UITaskBarEditor.cs create mode 100644 Editor/Form/UITaskBarEditor.cs.meta create mode 100644 Runtime/Form/Controllers/UITaskBar.cs create mode 100644 Runtime/Form/Controllers/UITaskBar.cs.meta create mode 100644 Runtime/Form/Controllers/UITaskBarItem.cs create mode 100644 Runtime/Form/Controllers/UITaskBarItem.cs.meta create mode 100644 Runtime/Form/WindowEntry.cs create mode 100644 Runtime/Form/WindowEntry.cs.meta create mode 100644 Runtime/Prefab.meta create mode 100644 Runtime/Prefab/Form.meta create mode 100644 Runtime/Prefab/Form/Window Title.prefab create mode 100644 Runtime/Prefab/Form/Window Title.prefab.meta diff --git a/Editor/Form/UIFormControllerEditor.cs b/Editor/Form/UIFormControllerEditor.cs index fb99b95..aa64846 100644 --- a/Editor/Form/UIFormControllerEditor.cs +++ b/Editor/Form/UIFormControllerEditor.cs @@ -1,5 +1,6 @@ using UnityEditor; using UnityEngine; +using XericUI.Form; namespace XericUIEditor.Form { @@ -24,8 +25,6 @@ namespace XericUIEditor.Form serializedObject.ApplyModifiedProperties(); } - #region 基本设置 - private void DrawBasicSection() { EditorGUILayout.LabelField("窗口基本设置", EditorStyles.boldLabel); @@ -33,10 +32,6 @@ namespace XericUIEditor.Form EditorGUILayout.Space(); } - #endregion - - #region 边缘行为 - private void DrawEdgeSection() { EditorGUILayout.LabelField("边缘吸附", EditorStyles.boldLabel); @@ -58,10 +53,6 @@ namespace XericUIEditor.Form EditorGUILayout.Space(); } - #endregion - - #region 调试与测试 - private void DrawDebugSection() { EditorGUILayout.LabelField("调试", EditorStyles.boldLabel); @@ -72,74 +63,49 @@ namespace XericUIEditor.Form // 当前状态 EditorGUILayout.LabelField("当前状态", EditorStyles.boldLabel); - var state = _controller.CurrentState; - Color stateColor; - string stateText; - - switch (state) + Color stateColor = state switch { - case XericUI.Form.WindowState.Normal: - stateColor = Color.white; - stateText = "● 窗口化 (Normal)"; - break; - case XericUI.Form.WindowState.Maximized: - stateColor = Color.green; - stateText = "■ 全屏 (Maximized)"; - break; - case XericUI.Form.WindowState.Minimized: - stateColor = Color.gray; - stateText = "○ 最小化 (Minimized)"; - break; - case XericUI.Form.WindowState.EdgeHidden: - stateColor = new Color(1f, 0.6f, 0f); - stateText = "◄ 边缘隐藏 (Edge Hidden)"; - break; - default: - stateColor = Color.white; - stateText = state.ToString(); - break; - } + XericUI.Form.WindowState.Normal => Color.white, + XericUI.Form.WindowState.Maximized => Color.green, + XericUI.Form.WindowState.Minimized => Color.gray, + XericUI.Form.WindowState.EdgeHidden => new Color(1f, 0.6f, 0f), + _ => Color.white + }; + var entries = _controller.Manager?.GetAllEntries(); + WindowEntry entry = _controller.Entry; + string extra = entry != null ? $" | ID={entry.Id}" : ""; GUI.color = stateColor; - EditorGUILayout.LabelField(stateText, EditorStyles.miniBoldLabel); + EditorGUILayout.LabelField($"{state}{extra}", EditorStyles.miniBoldLabel); GUI.color = Color.white; EditorGUILayout.Space(); - // 手动测试按钮 + // 手动测试 EditorGUILayout.LabelField("手动测试", EditorStyles.boldLabel); - EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("全屏/还原")) { _controller.ToggleMaximize(); SceneView.RepaintAll(); } - if (GUILayout.Button("模拟进入")) + if (GUILayout.Button("置顶 (Focus)")) { - _controller.SimulateEnter(); - SceneView.RepaintAll(); - } - if (GUILayout.Button("模拟退出")) - { - _controller.SimulateExit(); + _controller.MoveToFront(); SceneView.RepaintAll(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); - // 调试提示 if (!Application.isPlaying && _controller.DebugMode) { EditorGUILayout.HelpBox( - "调试模式已启用 — 在 SceneView 中移动鼠标即可预览。\n" + - "蓝色边框 = 窗口范围 | 绿色 = 鼠标悬停中 | 橙色 = 吸附阈值范围", + "调试模式已启用 — SceneView 中移动鼠标实时预览。\n" + + "蓝色边框=窗口范围 | 绿色=鼠标悬停中", MessageType.Info); } } - - #endregion } } diff --git a/Editor/Form/UITaskBarEditor.cs b/Editor/Form/UITaskBarEditor.cs new file mode 100644 index 0000000..472072e --- /dev/null +++ b/Editor/Form/UITaskBarEditor.cs @@ -0,0 +1,40 @@ +using UnityEditor; +using UnityEngine; + +namespace XericUIEditor.Form +{ + [CustomEditor(typeof(XericUI.Form.UITaskBar))] + public class UITaskBarEditor : Editor + { + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.LabelField("任务栏绑定", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Manager"), new GUIContent("窗口管理器 (Manager)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ItemPrefab"), new GUIContent("图标预制体 (Item Prefab)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ItemsContainer"), new GUIContent("图标容器 (Container)")); + + serializedObject.ApplyModifiedProperties(); + } + } + + [CustomEditor(typeof(XericUI.Form.UITaskBarItem))] + public class UITaskBarItemEditor : Editor + { + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.LabelField("任务栏图标项控件", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Icon"), new GUIContent("图标 (Icon)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_TitleText"), new GUIContent("标题文本 (Title)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ProgressBar"), new GUIContent("进度条 (Progress Bar)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Highlight"), new GUIContent("聚焦高亮 (Highlight)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Button"), new GUIContent("按钮 (Button)")); + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Form/UITaskBarEditor.cs.meta b/Editor/Form/UITaskBarEditor.cs.meta new file mode 100644 index 0000000..d0f6a15 --- /dev/null +++ b/Editor/Form/UITaskBarEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0dfbe31f222f10a4883ad6016cdad32b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Form/UITitleBarEditor.cs b/Editor/Form/UITitleBarEditor.cs index e458dd0..3c07fd8 100644 --- a/Editor/Form/UITitleBarEditor.cs +++ b/Editor/Form/UITitleBarEditor.cs @@ -6,26 +6,20 @@ namespace XericUIEditor.Form [CustomEditor(typeof(XericUI.Form.UITitleBar))] public class UITitleBarEditor : Editor { - private XericUI.Form.UITitleBar _titleBar; - - private void OnEnable() - { - _titleBar = (XericUI.Form.UITitleBar)target; - } - public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.LabelField("标题栏控件引用", EditorStyles.boldLabel); EditorGUILayout.HelpBox( - "将标题栏下的子控件拖入下方槽位即可。\n" + - "UITitleBar 会在 Start 时自动向上查找父级的 UIFormController 并绑定事件。", + "将标题栏下的子控件拖入下方槽位。\n" + + "UITitleBar 在 Start 时向上查找父级 UIFormController 并绑定事件。", MessageType.Info); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_IconImage"), new GUIContent("窗口图标 (Icon Image)")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_TitleText"), new GUIContent("标题文本 (Title Text)")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CloseButton"), new GUIContent("关闭按钮 (Close)")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MaximizeButton"), new GUIContent("最大化按钮 (Maximize)")); + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MaximizeToggle"), new GUIContent("最大化 Toggle (Maximize)")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MinimizeButton"), new GUIContent("最小化按钮 (Minimize)")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoSnapToggle"), new GUIContent("吸附开关 (Auto Snap)")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoHideToggle"), new GUIContent("隐藏开关 (Auto Hide)")); diff --git a/Runtime/Form/Controllers/UIFormController.cs b/Runtime/Form/Controllers/UIFormController.cs index 7ba8998..f5f00d2 100644 --- a/Runtime/Form/Controllers/UIFormController.cs +++ b/Runtime/Form/Controllers/UIFormController.cs @@ -7,7 +7,6 @@ namespace XericUI.Form { #region 枚举与结构体 - ///

范围坐标空间枚举 public enum FormRangeSpace { RelativeOffset, @@ -22,7 +21,7 @@ namespace XericUI.Form [Tooltip("坐标空间")] public FormRangeSpace space; - [Tooltip("范围定义。\nRelativeOffset: x,y=像素偏移; w,h=像素宽高(0=用参考尺寸)\nCanvasLocal: Canvas局部坐标\nAnchorToSelf: x,y=锚点0-1; w,h=尺寸比例0-1\nAnchorToCanvas: x,y=锚点0-1; w,h=尺寸比例0-1")] + [Tooltip("范围定义")] public Rect rect; } @@ -30,31 +29,31 @@ namespace XericUI.Form /// /// UI 窗体控制器 — 挂载到窗口根 GameObject 上,自身即为完整窗口。 - /// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动。 - /// 标题栏功能由独立的 UITitleBar 子组件提供,Controller 只提供公开 API。 + /// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动、置顶。 + /// 通过 WindowEntry 与 Manager 通信。 /// [AddComponentMenu("Xeric UI Vessel/Form/UI Form Controller")] [RequireComponent(typeof(RectTransform))] - public class UIFormController : MonoBehaviour, IUIFormTarget, IBeginDragHandler, IDragHandler, IEndDragHandler + public class UIFormController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler { #region 序列化字段 [Header("窗口基本设置")] - [SerializeField, Tooltip("窗口标题,供外部读取")] + [SerializeField, Tooltip("窗口标题")] private string m_WindowTitle = "Window"; [Header("边缘吸附")] - [SerializeField, Tooltip("是否启用边缘自动吸附")] + [SerializeField, Tooltip("启用边缘自动吸附")] private bool m_EnableAutoSnap = true; - [SerializeField, Tooltip("吸附阈值(像素),窗口距边缘小于此值时自动贴边")] + [SerializeField, Tooltip("吸附阈值(像素)")] private float m_SnapThreshold = 8f; [Header("自动隐藏")] - [SerializeField, Tooltip("是否启用自动隐藏(贴边后滑出屏幕)")] + [SerializeField, Tooltip("启用自动隐藏")] private bool m_EnableAutoHide; - [SerializeField, Tooltip("自动隐藏的滑动方向")] + [SerializeField, Tooltip("自动隐藏滑动方向")] private AutoHideDirection m_AutoHideDirection = AutoHideDirection.Left; [Header("调试")] @@ -69,28 +68,29 @@ namespace XericUI.Form #region 公开属性 public string WindowTitle => m_WindowTitle; - - /// 是否处于全屏状态 public bool IsMaximized => _windowState == WindowState.Maximized; - - /// 是否处于边缘隐藏状态 public bool IsEdgeHidden => _windowState == WindowState.EdgeHidden; - - /// 当前窗口状态 public WindowState CurrentState => _windowState; - - /// 自动吸附是否开启 public bool AutoSnapEnabled => m_EnableAutoSnap; - - /// 自动隐藏是否开启 public bool AutoHideEnabled => m_EnableAutoHide; - - /// 自动隐藏方向 public AutoHideDirection HideDirection => m_AutoHideDirection; - - /// 编辑器调试模式 public bool DebugMode => m_DebugMode; + /// 窗口数据 Entry(由 Manager 在 CreateWindow 时注入) + public WindowEntry Entry + { + get => _entry; + set + { + _entry = value; + if (_entry != null) + m_WindowTitle = _entry.Title; + } + } + + /// 父级 Manager 引用 + public UIFormManager Manager => _manager; + #endregion #region 内部状态 @@ -98,13 +98,15 @@ namespace XericUI.Form private RectTransform _rectTransform; private Canvas _canvas; private RectTransform _canvasRt; + private UIFormManager _manager; + private WindowEntry _entry; private WindowState _windowState = WindowState.Normal; // 拖拽 private Vector2 _dragOffset; - // 全屏还原数据 + // 全屏还原 private Vector2 _savePos; private Vector2 _saveSize; private Vector2 _saveAnchorMin; @@ -125,110 +127,72 @@ namespace XericUI.Form protected virtual void Awake() { _rectTransform = (RectTransform)transform; + _manager = GetComponentInParent(); _canvas = GetComponentInParent(); _canvasRt = _canvas != null ? (RectTransform)_canvas.transform : null; } protected virtual void OnEnable() { - if (UIFormManager.Main != null) - UIFormManager.Main.RegisterWindow(this); } protected virtual void OnDisable() { StopAutoHideMonitor(); - if (_windowState == WindowState.EdgeHidden && UIFormManager.Main != null) - UIFormManager.Main.UnregisterEdgeHidden(this); - - if (UIFormManager.Main != null) - { - UIFormManager.Main.UnregisterWindow(this); - UIFormManager.Main.UnregisterEdgeHidden(this); - } + if (_windowState == WindowState.EdgeHidden && _manager != null) + _manager.UnregisterEdgeHidden(_entry); +#if UNITY_EDITOR UnsubscribeEditorUpdate(); +#endif _isHovering = false; } #endregion - #region IUIFormTarget 实现 + #region === 公开 API === - public bool IsActive => isActiveAndEnabled; - - public Rect GetEnterRect() => GetWindowScreenRect(); - public Rect GetExitRect() => GetWindowScreenRect(); - - public void OnPointerEnter() - { - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 鼠标进入窗口范围: {name}", this); - } - - public void OnPointerExit() - { - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 鼠标离开窗口范围: {name}", this); - } - - #endregion - - #region === 公开 API(供 UITitleBar 及外部调用) === - - /// 全屏/还原切换 public void ToggleMaximize() { if (IsMaximized) RestoreFromFullscreen(); else MaximizeToFullscreen(); + + if (_entry != null) + _entry.State = _windowState; } - /// 最小化窗口 public void Minimize() { if (_windowState == WindowState.Minimized) return; - if (UIFormManager.Main == null) return; - - UIFormManager.Main.MinimizeWindow(this); + if (_manager == null || _entry == null) return; + _manager.MinimizeWindow(_entry.Id); } - /// 关闭窗口(Destroy GameObject) public void Close() { - if (UIFormManager.Main != null) - UIFormManager.Main.CloseWindow(this); - - if (Application.isPlaying) + if (_manager != null && _entry != null) + _manager.DestroyWindow(_entry.Id); + else if (Application.isPlaying) Destroy(gameObject); - else - DestroyImmediate(gameObject); } - /// 设置边缘吸附开关 - public void SetAutoSnapEnabled(bool enabled) - { - m_EnableAutoSnap = enabled; - } - - /// 设置自动隐藏开关 + public void SetAutoSnapEnabled(bool enabled) => m_EnableAutoSnap = enabled; public void SetAutoHideEnabled(bool enabled) { m_EnableAutoHide = enabled; - if (!enabled && _windowState == WindowState.EdgeHidden) - { - // 关闭自动隐藏 → 还原窗口 SlideInFromEdge(); - } } - /// 设置自动隐藏方向 - public void SetAutoHideDirection(AutoHideDirection direction) + public void SetAutoHideDirection(AutoHideDirection direction) => m_AutoHideDirection = direction; + + /// 置顶窗口 + public void MoveToFront() { - m_AutoHideDirection = direction; + transform.SetAsLastSibling(); } #endregion @@ -250,9 +214,6 @@ namespace XericUI.Form _rectTransform.offsetMax = Vector2.zero; _windowState = WindowState.Maximized; - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 窗口全屏: {name}", this); } private void RestoreFromFullscreen() @@ -265,25 +226,27 @@ namespace XericUI.Form _rectTransform.sizeDelta = _saveSize; _windowState = WindowState.Normal; - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 窗口还原: {name}", this); } #endregion - #region === 拖拽 === + #region === 拖拽 & 点击聚焦 === + + public void OnPointerDown(PointerEventData eventData) + { + // 点击窗口任意位置 → 置顶 + transform.SetAsLastSibling(); + if (_manager != null && _entry != null) + _manager.MoveToFront(_entry.Id); + } public void OnBeginDrag(PointerEventData eventData) { - if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) - return; + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return; - // 全屏拖拽 → 先还原 if (IsMaximized) { RestoreFromFullscreen(); - // 将窗口移到鼠标附近 _rectTransform.anchoredPosition = eventData.position; } @@ -292,8 +255,7 @@ namespace XericUI.Form public void OnDrag(PointerEventData eventData) { - if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) - return; + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return; _rectTransform.anchoredPosition = eventData.position + _dragOffset; @@ -303,14 +265,10 @@ namespace XericUI.Form public void OnEndDrag(PointerEventData eventData) { - if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) - return; + if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return; - // 拖拽结束 → 如果贴边且启用自动隐藏,则触发 if (m_EnableAutoHide && IsSnappedToEdge()) - { AutoHideToEdge(); - } } #endregion @@ -320,54 +278,35 @@ namespace XericUI.Form private void CheckEdgeSnap() { if (_canvasRt == null) return; - Vector2 canvasSize = _canvasRt.rect.size; Vector2 pos = _rectTransform.anchoredPosition; Vector2 size = _rectTransform.rect.size; - bool snapped = false; - - // 左 if (Mathf.Abs(pos.x) < m_SnapThreshold) - { pos.x = 0; - snapped = true; - } - // 右 else if (Mathf.Abs(pos.x + size.x - canvasSize.x) < m_SnapThreshold) - { pos.x = canvasSize.x - size.x; - snapped = true; - } _rectTransform.anchoredPosition = pos; } - /// 检测窗口是否贴边 private bool IsSnappedToEdge() { if (_canvasRt == null) return false; - Vector2 canvasSize = _canvasRt.rect.size; Vector2 pos = _rectTransform.anchoredPosition; Vector2 size = _rectTransform.rect.size; - - return Mathf.Abs(pos.x) < 1f - || Mathf.Abs(pos.x + size.x - canvasSize.x) < 1f; + return Mathf.Abs(pos.x) < 1f || Mathf.Abs(pos.x + size.x - canvasSize.x) < 1f; } - /// 获取最近的边缘方向 public AutoHideDirection GetClosestEdge() { if (_canvasRt == null) return AutoHideDirection.Left; - Vector2 canvasSize = _canvasRt.rect.size; Vector2 pos = _rectTransform.anchoredPosition; Vector2 size = _rectTransform.rect.size; - float distLeft = Mathf.Abs(pos.x); float distRight = Mathf.Abs(pos.x + size.x - canvasSize.x); - return distLeft < distRight ? AutoHideDirection.Left : AutoHideDirection.Right; } @@ -377,8 +316,7 @@ namespace XericUI.Form private void AutoHideToEdge() { - if (_windowState == WindowState.EdgeHidden) return; - if (_canvasRt == null) return; + if (_windowState == WindowState.EdgeHidden || _canvasRt == null) return; Vector2 canvasSize = _canvasRt.rect.size; Vector2 pos = _rectTransform.anchoredPosition; @@ -390,58 +328,40 @@ namespace XericUI.Form if (dir == AutoHideDirection.Left || dir == AutoHideDirection.Right) dir = GetClosestEdge(); - switch (dir) + _edgeHiddenPos = dir switch { - case AutoHideDirection.Left: - _edgeHiddenPos = new Vector2(-size.x, pos.y); - break; - case AutoHideDirection.Right: - _edgeHiddenPos = new Vector2(canvasSize.x, pos.y); - break; - case AutoHideDirection.Down: - _edgeHiddenPos = new Vector2(pos.x, -size.y); - break; - case AutoHideDirection.Up: - _edgeHiddenPos = new Vector2(pos.x, canvasSize.y); - break; - } + AutoHideDirection.Left => new Vector2(-size.x, pos.y), + AutoHideDirection.Right => new Vector2(canvasSize.x, pos.y), + AutoHideDirection.Down => new Vector2(pos.x, -size.y), + AutoHideDirection.Up => new Vector2(pos.x, canvasSize.y), + _ => pos + }; _rectTransform.anchoredPosition = _edgeHiddenPos; _windowState = WindowState.EdgeHidden; - if (UIFormManager.Main != null) - UIFormManager.Main.RegisterEdgeHidden(this, dir); - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 自动隐藏 (方向={dir}): {name}", this); + if (_manager != null && _entry != null) + _manager.RegisterEdgeHidden(_entry, dir); } - /// 由 Manager 调用 — 从边缘滑出 public void SlideInFromEdge() { if (_windowState != WindowState.EdgeHidden) return; - StopAutoHideMonitor(); _rectTransform.anchoredPosition = _edgeVisiblePos; _windowState = WindowState.Normal; - if (UIFormManager.Main != null) - UIFormManager.Main.UnregisterEdgeHidden(this); + if (_manager != null) + _manager.UnregisterEdgeHidden(_entry); - // 开始监控鼠标是否离开窗口 _autoHideCoroutine = StartCoroutine(MonitorMouseExit()); - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 从边缘滑出: {name}", this); } - /// 监控鼠标是否离开窗口范围,离开则自动隐藏 private IEnumerator MonitorMouseExit() { while (_windowState == WindowState.Normal && m_EnableAutoHide) { yield return null; - Rect screenRect = GetWindowScreenRect(); if (!screenRect.Contains(Input.mousePosition)) { @@ -464,82 +384,29 @@ namespace XericUI.Form #region === 窗口屏幕范围 === - /// 获取窗口自身在屏幕空间中的 Rect public Rect GetWindowScreenRect() { if (_rectTransform == null || _canvas == null) return Rect.zero; - Vector3[] corners = new Vector3[4]; _rectTransform.GetWorldCorners(corners); - Camera cam = _canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera; - - Vector2 min = Vector2.positiveInfinity; - Vector2 max = Vector2.negativeInfinity; - + Vector2 min = Vector2.positiveInfinity, max = Vector2.negativeInfinity; for (int i = 0; i < 4; i++) { Vector2 sp = cam != null ? RectTransformUtility.WorldToScreenPoint(cam, corners[i]) : (Vector2)corners[i]; - min = Vector2.Min(min, sp); max = Vector2.Max(max, sp); } - return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); } #endregion - #region === 悬停检测(供 Manager 调用) === - - public bool CheckHover(Vector2 screenPoint) - { - Rect windowRect = GetWindowScreenRect(); - - if (!_isHovering) - { - if (windowRect.Contains(screenPoint)) - { - _isHovering = true; - OnPointerEnter(); - } - } - else - { - if (!windowRect.Contains(screenPoint)) - { - _isHovering = false; - OnPointerExit(); - } - } - - return _isHovering; - } - - public void SimulateEnter() - { - _isHovering = true; - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 模拟进入: {name}", this); - } - - public void SimulateExit() - { - _isHovering = false; - - if (m_DebugLogEvents) - Debug.Log($"[UIFormController] 模拟退出: {name}", this); - } - - #endregion - #region 编辑器调试 #if UNITY_EDITOR - [System.NonSerialized] private bool _editorDebugSubscribed; [System.NonSerialized] private Vector2 _editorMouseScreenPos; @@ -559,87 +426,53 @@ namespace XericUI.Form protected virtual void OnValidate() { - if (m_DebugMode) - SubscribeEditorUpdate(); - else - UnsubscribeEditorUpdate(); + if (m_DebugMode) SubscribeEditorUpdate(); + else UnsubscribeEditorUpdate(); } - /// - /// SceneView 重绘回调 — 在 GUI 上下文中获取鼠标屏幕坐标并执行悬停检测。 - /// 使用 GUIUtility.GUIToScreenPoint 将 SceneView 内的 GUI 坐标转为 Unity 屏幕坐标。 - /// private void OnSceneGUI(UnityEditor.SceneView sceneView) { - if (this == null) - { - UnsubscribeEditorUpdate(); - return; - } + if (this == null) { UnsubscribeEditorUpdate(); return; } + if (Application.isPlaying || !m_DebugMode) return; - if (Application.isPlaying) return; - if (!m_DebugMode) return; - - // Event.current.mousePosition 是相对于 SceneView 窗口的 GUI 坐标 - // GUIUtility.GUIToScreenPoint 将其转换为屏幕坐标(左下角为原点) Vector2 guiPos = Event.current.mousePosition; _editorMouseScreenPos = GUIUtility.GUIToScreenPoint(guiPos); - CheckHover(_editorMouseScreenPos); + Rect rect = GetWindowScreenRect(); + _isHovering = rect.Contains(_editorMouseScreenPos); } protected virtual void OnDrawGizmosSelected() { - if (!m_DebugMode) return; - if (_canvas == null) return; - if (_canvasRt == null) return; - + if (!m_DebugMode || _canvas == null || _canvasRt == null) return; Rect screenRect = GetWindowScreenRect(); if (screenRect.width <= 0 || screenRect.height <= 0) return; - // 悬停时高亮边框 Color borderColor = _isHovering ? new Color(0f, 1f, 0f, 0.6f) : new Color(0f, 0.7f, 1f, 0.3f); - Gizmos.color = borderColor; DrawScreenRectOnCanvas(screenRect, borderColor, _canvas, _canvasRt); - - // 边缘吸附阈值可视化 - if (m_EnableAutoSnap) - { - Gizmos.color = new Color(1f, 0.6f, 0f, 0.15f); - DrawScreenRectOnCanvas( - new Rect(screenRect.x + m_SnapThreshold, screenRect.y, - screenRect.width - m_SnapThreshold * 2, screenRect.height), - Gizmos.color, _canvas, _canvasRt); - } } private static void DrawScreenRectOnCanvas(Rect screenRect, Color color, Canvas canvas, RectTransform canvasRt) { if (screenRect.width <= 0 || screenRect.height <= 0) return; - Gizmos.color = color; Camera uiCam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; - - Vector3 p0 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMin), uiCam, canvasRt); - Vector3 p1 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMin), uiCam, canvasRt); - Vector3 p2 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMax), uiCam, canvasRt); - Vector3 p3 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMax), uiCam, canvasRt); - - Gizmos.DrawLine(p0, p1); - Gizmos.DrawLine(p1, p2); - Gizmos.DrawLine(p2, p3); - Gizmos.DrawLine(p3, p0); + Vector3 p0 = Stw(new Vector3(screenRect.xMin, screenRect.yMin), uiCam, canvasRt); + Vector3 p1 = Stw(new Vector3(screenRect.xMax, screenRect.yMin), uiCam, canvasRt); + Vector3 p2 = Stw(new Vector3(screenRect.xMax, screenRect.yMax), uiCam, canvasRt); + Vector3 p3 = Stw(new Vector3(screenRect.xMin, screenRect.yMax), uiCam, canvasRt); + Gizmos.DrawLine(p0, p1); Gizmos.DrawLine(p1, p2); + Gizmos.DrawLine(p2, p3); Gizmos.DrawLine(p3, p0); } - private static Vector3 ScreenToWorldOnCanvas(Vector3 screenPoint, Camera uiCamera, RectTransform canvasRt) + private static Vector3 Stw(Vector3 sp, Camera cam, RectTransform rt) { - RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRt, (Vector2)screenPoint, uiCamera, out Vector3 wp); + RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, (Vector2)sp, cam, out Vector3 wp); return wp; } - #endif #endregion diff --git a/Runtime/Form/Controllers/UITaskBar.cs b/Runtime/Form/Controllers/UITaskBar.cs new file mode 100644 index 0000000..4b33c71 --- /dev/null +++ b/Runtime/Form/Controllers/UITaskBar.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XericUI.Form +{ + /// + /// 任务栏控制器 — 监听 UIFormManager 的窗口创建/销毁/状态变更事件, + /// 动态创建/销毁 UITaskBarItem 图标。 + /// + public class UITaskBar : MonoBehaviour + { + [Header("绑定")] + [SerializeField, Tooltip("窗口管理器引用")] + private UIFormManager m_Manager; + + [SerializeField, Tooltip("任务栏图标项预制体")] + private UITaskBarItem m_ItemPrefab; + + [SerializeField, Tooltip("图标项的父容器")] + private Transform m_ItemsContainer; + + private readonly Dictionary _items = new Dictionary(); + + #region 生命周期 + + private void Start() + { + if (m_Manager == null) + { + Debug.LogError("[UITaskBar] 未绑定 UIFormManager!", this); + return; + } + + m_Manager.OnWindowCreated += OnWindowCreated; + m_Manager.OnWindowDestroyed += OnWindowDestroyed; + m_Manager.OnWindowStateChanged += OnWindowStateChanged; + } + + private void OnDestroy() + { + if (m_Manager != null) + { + m_Manager.OnWindowCreated -= OnWindowCreated; + m_Manager.OnWindowDestroyed -= OnWindowDestroyed; + m_Manager.OnWindowStateChanged -= OnWindowStateChanged; + } + + foreach (var item in _items.Values) + { + if (item != null) + Destroy(item.gameObject); + } + _items.Clear(); + } + + #endregion + + #region 事件回调 + + private void OnWindowCreated(WindowEntry entry) + { + if (entry == null || !entry.ShowInTaskBar) return; + if (m_ItemPrefab == null || m_ItemsContainer == null) return; + + var item = Instantiate(m_ItemPrefab, m_ItemsContainer); + item.Bind(entry, m_Manager); + _items[entry.Id] = item; + } + + private void OnWindowDestroyed(WindowEntry entry) + { + if (entry == null) return; + + if (_items.TryGetValue(entry.Id, out var item)) + { + if (item != null) + Destroy(item.gameObject); + _items.Remove(entry.Id); + } + } + + private void OnWindowStateChanged(WindowEntry entry) + { + if (entry == null) return; + + // 状态变更时触发 Repaint(进度、高亮等由 Item 自己的 Update 处理) + } + + #endregion + } +} diff --git a/Runtime/Form/Controllers/UITaskBar.cs.meta b/Runtime/Form/Controllers/UITaskBar.cs.meta new file mode 100644 index 0000000..abbecb6 --- /dev/null +++ b/Runtime/Form/Controllers/UITaskBar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08764fa4decbd8b4b9f16f7236d36baf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/UITaskBarItem.cs b/Runtime/Form/Controllers/UITaskBarItem.cs new file mode 100644 index 0000000..c4bbff2 --- /dev/null +++ b/Runtime/Form/Controllers/UITaskBarItem.cs @@ -0,0 +1,108 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace XericUI.Form +{ + /// + /// 任务栏图标项 — 显示单个窗口的图标、标题、进度条和聚焦高亮。 + /// 点击时通过 UIFormManager 切换窗口显示/隐藏并置顶。 + /// + public class UITaskBarItem : MonoBehaviour + { + [Header("控件引用")] + [SerializeField, Tooltip("窗口图标")] + private Image m_Icon; + + [SerializeField, Tooltip("窗口标题文本")] + private Text m_TitleText; + + [SerializeField, Tooltip("进度条 Image(fillAmount)")] + private Image m_ProgressBar; + + [SerializeField, Tooltip("聚焦高亮 Image")] + private Image m_Highlight; + + [SerializeField, Tooltip("按钮组件")] + private Button m_Button; + + private WindowEntry _entry; + private UIFormManager _manager; + + public WindowEntry Entry => _entry; + + #region 初始化 + + public void Bind(WindowEntry entry, UIFormManager manager) + { + _entry = entry; + _manager = manager; + + if (m_Icon != null && entry.Icon != null) + m_Icon.sprite = entry.Icon; + + if (m_Icon != null) + m_Icon.color = entry.TintColor; + + if (m_TitleText != null) + m_TitleText.text = entry.Title; + + if (m_Button != null) + m_Button.onClick.AddListener(OnClick); + + RefreshHighlight(); + RefreshProgress(); + } + + private void OnDestroy() + { + if (m_Button != null) + m_Button.onClick.RemoveListener(OnClick); + } + + #endregion + + #region 交互 + + private void OnClick() + { + if (_entry == null || _manager == null) return; + + _manager.ToggleWindow(_entry.Id); + _manager.MoveToFront(_entry.Id); + } + + #endregion + + #region 每帧同步 + + private void Update() + { + if (_entry == null) return; + + if (m_TitleText != null && m_TitleText.text != _entry.Title) + m_TitleText.text = _entry.Title; + + if (m_Icon != null && m_Icon.color != _entry.TintColor) + m_Icon.color = _entry.TintColor; + + RefreshProgress(); + RefreshHighlight(); + } + + private void RefreshProgress() + { + if (m_ProgressBar == null) return; + m_ProgressBar.fillAmount = _entry.Progress; + m_ProgressBar.gameObject.SetActive(_entry.ShowProgress && _entry.Progress > 0f); + } + + private void RefreshHighlight() + { + if (m_Highlight == null || _manager == null) return; + var focused = _manager.GetFocusedEntry(); + m_Highlight.gameObject.SetActive(focused == _entry); + } + + #endregion + } +} diff --git a/Runtime/Form/Controllers/UITaskBarItem.cs.meta b/Runtime/Form/Controllers/UITaskBarItem.cs.meta new file mode 100644 index 0000000..dc0b6aa --- /dev/null +++ b/Runtime/Form/Controllers/UITaskBarItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf298c8b67bb4d345a69728112b72229 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Form/Controllers/UITitleBar.cs b/Runtime/Form/Controllers/UITitleBar.cs index 52b3b48..e054aea 100644 --- a/Runtime/Form/Controllers/UITitleBar.cs +++ b/Runtime/Form/Controllers/UITitleBar.cs @@ -12,21 +12,23 @@ namespace XericUI.Form { /// /// 窗口标题栏 — 独立子组件,挂载在 UIFormController 的子对象上。 - /// Start 时向上查找父级 UIFormController,绑定按钮事件到其公开 API。 - /// 与窗口完全解耦:Controller 不关心标题栏是否存在。 + /// Start 时向上查找父级 UIFormController,绑定按钮/Toggle 事件到其公开 API。 /// [AddComponentMenu("Xeric UI Vessel/Form/UI Title Bar")] public class UITitleBar : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { [Header("控件引用")] + [SerializeField, Tooltip("窗口图标")] + private Image m_IconImage; + [SerializeField, Tooltip("标题文本")] private UI_Text m_TitleText; [SerializeField, Tooltip("关闭按钮")] private Button m_CloseButton; - [SerializeField, Tooltip("最大化按钮")] - private Button m_MaximizeButton; + [SerializeField, Tooltip("最大化 Toggle(isOn=全屏)")] + private Toggle m_MaximizeToggle; [SerializeField, Tooltip("最小化按钮")] private Button m_MinimizeButton; @@ -57,9 +59,25 @@ namespace XericUI.Form { if (_controller == null) return; - // 实时同步标题(可能由外部修改) if (m_TitleText != null && m_TitleText.text != _controller.WindowTitle) m_TitleText.text = _controller.WindowTitle; + + // 同步最大化 Toggle 状态 + if (m_MaximizeToggle != null) + { + bool maxed = _controller.IsMaximized; + if (m_MaximizeToggle.isOn != maxed) + m_MaximizeToggle.SetIsOnWithoutNotify(maxed); + } + + // 同步图标 + if (m_IconImage != null && _controller.Entry != null) + { + if (_controller.Entry.Icon != null && m_IconImage.sprite != _controller.Entry.Icon) + m_IconImage.sprite = _controller.Entry.Icon; + if (m_IconImage.color != _controller.Entry.TintColor) + m_IconImage.color = _controller.Entry.TintColor; + } } #region 事件绑定 @@ -69,8 +87,8 @@ namespace XericUI.Form if (m_CloseButton != null) m_CloseButton.onClick.AddListener(() => _controller.Close()); - if (m_MaximizeButton != null) - m_MaximizeButton.onClick.AddListener(() => _controller.ToggleMaximize()); + if (m_MaximizeToggle != null) + m_MaximizeToggle.onValueChanged.AddListener(_ => _controller.ToggleMaximize()); if (m_MinimizeButton != null) m_MinimizeButton.onClick.AddListener(() => _controller.Minimize()); @@ -119,11 +137,21 @@ namespace XericUI.Form if (m_TitleText != null) m_TitleText.text = _controller.WindowTitle; + if (m_MaximizeToggle != null) + m_MaximizeToggle.isOn = _controller.IsMaximized; + if (m_AutoSnapToggle != null) m_AutoSnapToggle.isOn = _controller.AutoSnapEnabled; if (m_AutoHideToggle != null) m_AutoHideToggle.isOn = _controller.AutoHideEnabled; + + // 图标 + if (m_IconImage != null && _controller.Entry != null && _controller.Entry.Icon != null) + { + m_IconImage.sprite = _controller.Entry.Icon; + m_IconImage.color = _controller.Entry.TintColor; + } } #endregion diff --git a/Runtime/Form/UIFormManager.cs b/Runtime/Form/UIFormManager.cs index f02d048..8e19af0 100644 --- a/Runtime/Form/UIFormManager.cs +++ b/Runtime/Form/UIFormManager.cs @@ -4,91 +4,57 @@ using UnityEngine; namespace XericUI.Form { /// - /// UI 窗体管理器 — 窗口管理中心。 - /// 挂载在 Canvas 所在 GameObject 上(或通过代码自动创建), - /// 管理所有窗口的注册、最小化恢复、边缘触发隐藏窗口的显隐调度。 + /// UI 窗体管理器 — 窗口管理中心。挂载在 Canvas 下作为所有窗口的父容器。 + /// 通过 Entry 驱动管理窗口生命周期、层级排序、委托事件发布。 + /// 支持任务栏和边缘触发隐藏窗口。 /// [AddComponentMenu("Xeric UI Vessel/Form/UI Form Manager")] [DefaultExecutionOrder(32000)] public class UIFormManager : MonoBehaviour { [Header("边缘触发")] - [SerializeField, Tooltip("边缘触发区域大小(x=宽度方向,y=高度方向),鼠标进入此区域时触发对应方向隐藏窗口的显示")] + [SerializeField, Tooltip("边缘触发区域大小")] private Vector2 m_EdgeTriggerSize = new Vector2(80, 60); [Header("调试")] [SerializeField, Tooltip("输出事件日志")] private bool m_EnableDebugLog; - #region 静态单例 + #region === 委托事件 === - private static UIFormManager s_Main; + /// 窗口创建 + public event WindowEntryEventHandler OnWindowCreated; - /// 全局单例引用 - public static UIFormManager Main => s_Main; + /// 窗口销毁 + public event WindowEntryEventHandler OnWindowDestroyed; + + /// 窗口状态变更 + public event WindowEntryEventHandler OnWindowStateChanged; + + /// 窗口获得焦点(置顶) + public event WindowEntryEventHandler OnWindowFocused; #endregion - #region 内部状态 + #region === 内部状态 === - // 所有窗口 - private readonly List _windows = new List(); + private readonly Dictionary _entries = new Dictionary(); + private readonly List _orderedEntries = new List(); + private string _focusedEntryId; - // 最小化窗口数据 - private struct MinimizedData + // 边缘隐藏(按方向分组) + private readonly Dictionary> _edgeHidden = new() { - public Vector2 anchoredPosition; - public Vector2 sizeDelta; - public Vector2 anchorMin; - public Vector2 anchorMax; - } - private readonly Dictionary _minimized = new(); - - // 边缘隐藏窗口(按方向分组) - private readonly Dictionary> _edgeHidden = new() - { - { AutoHideDirection.Left, new List() }, - { AutoHideDirection.Right, new List() }, - { AutoHideDirection.Up, new List() }, - { AutoHideDirection.Down, new List() }, + { AutoHideDirection.Left, new List() }, + { AutoHideDirection.Right, new List() }, + { AutoHideDirection.Up, new List() }, + { AutoHideDirection.Down, new List() }, }; - - // 边缘触发状态跟踪(避免重复触发) private AutoHideDirection? _lastEdgeTrigger; #endregion - #region 生命周期 - - protected virtual void Awake() - { - if (s_Main != null && s_Main != this) - { - Debug.LogWarning($"[UIFormManager] 已存在单例,销毁重复实例: {name}", this); - Destroy(this); - return; - } - - s_Main = this; - } - - protected virtual void OnEnable() - { - if (s_Main == null) s_Main = this; - } - - protected virtual void OnDisable() - { - if (s_Main == this) s_Main = null; - } - - protected virtual void OnDestroy() - { - if (s_Main == this) s_Main = null; - _windows.Clear(); - _minimized.Clear(); - foreach (var list in _edgeHidden.Values) list.Clear(); - } + #region === 生命周期 === protected virtual void Update() { @@ -97,64 +63,222 @@ namespace XericUI.Form #endregion - #region === 窗口注册 / 注销 === + #region === 程序化窗口创建 / 销毁 === - public void RegisterWindow(UIFormController window) + /// 程序化创建一个窗口实例并返回其 Entry + public WindowEntry CreateWindow(RectTransform prefab, string title = null, Sprite icon = null) { - if (window == null || _windows.Contains(window)) return; - _windows.Add(window); + if (prefab == null) + { + Debug.LogError("[UIFormManager] CreateWindow: prefab 为空", this); + return null; + } + + var instance = Instantiate(prefab, transform); + instance.name = title ?? prefab.name; + + var controller = instance.GetComponent(); + var entry = new WindowEntry + { + Title = title ?? prefab.name, + Icon = icon, + Prefab = prefab, + Controller = controller, + State = WindowState.Normal, + }; + + if (controller != null) + controller.Entry = entry; + + _entries[entry.Id] = entry; + _orderedEntries.Add(entry); + + MoveToFront(entry.Id); + + NotifyCreated(entry); if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 注册窗口: {window.name}"); + Debug.Log($"[UIFormManager] 创建窗口: {entry.Title} (ID={entry.Id})"); + + return entry; } - public void UnregisterWindow(UIFormController window) + /// 销毁窗口及其所有子窗口 + public void DestroyWindow(string entryId) { - if (window == null) return; - _windows.Remove(window); - UnregisterEdgeHidden(window); - _minimized.Remove(window); + if (!_entries.TryGetValue(entryId, out var entry)) return; - if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 注销窗口: {window.name}"); + // 级联关闭子窗口 + CascadeCloseChildren(entry); + + UnregisterEdgeHiddenForEntry(entry); + + _orderedEntries.Remove(entry); + _entries.Remove(entryId); + + if (_focusedEntryId == entryId) + _focusedEntryId = null; + + var controller = entry.Controller; + entry.Controller = null; + + NotifyDestroyed(entry); + + if (controller != null && Application.isPlaying) + Destroy(controller.gameObject); + } + + private void CascadeCloseChildren(WindowEntry entry) + { + for (int i = entry.ChildEntries.Count - 1; i >= 0; i--) + { + var child = entry.ChildEntries[i]; + DestroyWindow(child.Id); + } } #endregion - #region === 边缘隐藏窗口管理 === + #region === 显示控制 === - /// 注册边缘隐藏窗口 - public void RegisterEdgeHidden(UIFormController window, AutoHideDirection direction) + /// 显示窗口并置顶 + public void ShowWindow(string entryId) { - if (window == null) return; + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + entry.Controller.gameObject.SetActive(true); + MoveToFront(entryId); + } + + /// 隐藏窗口 + public void HideWindow(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + + entry.Controller.gameObject.SetActive(false); + } + + /// 切换窗口显示/隐藏 + public void ToggleWindow(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + + if (entry.State == WindowState.Minimized) + { + RestoreWindow(entryId); + } + else if (entry.Controller.gameObject.activeSelf) + { + MinimizeWindow(entryId); + } + else + { + ShowWindow(entryId); + } + } + + /// 将窗口置顶 + public void MoveToFront(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + + entry.Controller.transform.SetAsLastSibling(); + + _orderedEntries.Remove(entry); + _orderedEntries.Add(entry); + + _focusedEntryId = entryId; + + NotifyFocused(entry); + } + + #endregion + + #region === 最小化 / 恢复 === + + /// 最小化窗口 + public void MinimizeWindow(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + if (entry.State == WindowState.Minimized) return; + + entry.Controller.gameObject.SetActive(false); + entry.State = WindowState.Minimized; + + NotifyStateChanged(entry); + } + + /// 恢复已最小化的窗口 + public void RestoreWindow(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + if (entry.State != WindowState.Minimized) return; + + ShowWindow(entryId); + entry.State = WindowState.Normal; + + NotifyStateChanged(entry); + } + + public void ToggleMaximize(string entryId) + { + if (!TryGetEntry(entryId, out var entry)) return; + if (entry.Controller == null) return; + + entry.Controller.ToggleMaximize(); + NotifyStateChanged(entry); + } + + #endregion + + #region === 查询 === + + public WindowEntry GetEntry(string id) + { + _entries.TryGetValue(id, out var entry); + return entry; + } + + public IReadOnlyList GetAllEntries() => _orderedEntries; + + public WindowEntry GetFocusedEntry() + { + if (string.IsNullOrEmpty(_focusedEntryId)) return null; + return GetEntry(_focusedEntryId); + } + + public int EntryCount => _entries.Count; + + #endregion + + #region === 边缘隐藏 === + + public void RegisterEdgeHidden(WindowEntry entry, AutoHideDirection direction) + { + if (entry == null) return; var list = _edgeHidden[direction]; - if (!list.Contains(window)) - list.Add(window); - - if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 边缘隐藏注册: {window.name} (方向={direction})"); + if (!list.Contains(entry)) + list.Add(entry); } - /// 注销边缘隐藏窗口 - public void UnregisterEdgeHidden(UIFormController window) + public void UnregisterEdgeHidden(WindowEntry entry) { - if (window == null) return; - + if (entry == null) return; foreach (var list in _edgeHidden.Values) - list.Remove(window); + list.Remove(entry); } - /// 获取指定方向的隐藏窗口列表 - public IReadOnlyList GetEdgeWindows(AutoHideDirection direction) + private void UnregisterEdgeHiddenForEntry(WindowEntry entry) { - return _edgeHidden[direction]; + UnregisterEdgeHidden(entry); } - #endregion - - #region === 边缘触发检测 === - private void CheckEdgeTriggers() { Vector2 mousePos = Input.mousePosition; @@ -167,124 +291,60 @@ namespace XericUI.Form bool topTrigger = mousePos.y > (sh - m_EdgeTriggerSize.y); AutoHideDirection? activeTrigger = null; - if (leftTrigger) activeTrigger = AutoHideDirection.Left; else if (rightTrigger) activeTrigger = AutoHideDirection.Right; else if (bottomTrigger) activeTrigger = AutoHideDirection.Down; else if (topTrigger) activeTrigger = AutoHideDirection.Up; - // 触发进入 → 显示该方向窗口 if (activeTrigger.HasValue && activeTrigger != _lastEdgeTrigger) { ShowEdgeWindows(activeTrigger.Value); } - // 鼠标离开所有边缘触发区 if (!activeTrigger.HasValue) - { _lastEdgeTrigger = null; - } } - /// 显示指定方向的所有边缘隐藏窗口 public void ShowEdgeWindows(AutoHideDirection direction) { _lastEdgeTrigger = direction; - var list = _edgeHidden[direction]; + for (int i = list.Count - 1; i >= 0; i--) { - var window = list[i]; - if (window == null) + var entry = list[i]; + if (entry?.Controller == null) { list.RemoveAt(i); continue; } - - window.SlideInFromEdge(); + entry.Controller.SlideInFromEdge(); } - - if (m_EnableDebugLog && list.Count > 0) - Debug.Log($"[UIFormManager] 边缘触发: 方向={direction}, 显示 {list.Count} 个窗口"); } #endregion - #region === 最小化 / 恢复 === + #region === 内部通知 === - internal void MinimizeWindow(UIFormController window) + private void NotifyCreated(WindowEntry entry) => OnWindowCreated?.Invoke(entry); + private void NotifyDestroyed(WindowEntry entry) => OnWindowDestroyed?.Invoke(entry); + private void NotifyStateChanged(WindowEntry entry) => OnWindowStateChanged?.Invoke(entry); + private void NotifyFocused(WindowEntry entry) => OnWindowFocused?.Invoke(entry); + + #endregion + + #region === 工具 === + + private bool TryGetEntry(string id, out WindowEntry entry) { - if (window == null || _minimized.ContainsKey(window)) return; - - var rt = window.transform as RectTransform; - if (rt == null) return; - - _minimized[window] = new MinimizedData + if (string.IsNullOrEmpty(id)) { - anchoredPosition = rt.anchoredPosition, - sizeDelta = rt.sizeDelta, - anchorMin = rt.anchorMin, - anchorMax = rt.anchorMax, - }; - - window.gameObject.SetActive(false); - - if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 最小化窗口: {window.name}"); + entry = null; + return false; + } + return _entries.TryGetValue(id, out entry); } - /// 恢复已最小化的窗口 - public void RestoreWindow(UIFormController window) - { - if (window == null || !_minimized.TryGetValue(window, out var data)) return; - - var rt = window.transform as RectTransform; - if (rt == null) return; - - rt.anchorMin = data.anchorMin; - rt.anchorMax = data.anchorMax; - rt.anchoredPosition = data.anchoredPosition; - rt.sizeDelta = data.sizeDelta; - - window.gameObject.SetActive(true); - _minimized.Remove(window); - - if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 恢复窗口: {window.name}"); - } - - /// 获取所有已最小化的窗口列表 - public IReadOnlyList GetMinimizedWindows() - { - return new List(_minimized.Keys); - } - - #endregion - - #region === 关闭 === - - internal void CloseWindow(UIFormController window) - { - if (window == null) return; - - _windows.Remove(window); - UnregisterEdgeHidden(window); - _minimized.Remove(window); - - if (m_EnableDebugLog) - Debug.Log($"[UIFormManager] 关闭窗口: {window.name}"); - } - - #endregion - - #region === 查询 === - - /// 已注册窗口数量 - public int WindowCount => _windows.Count; - - /// 获取所有已注册窗口 - public IReadOnlyList GetAllWindows() => _windows; - #endregion } } diff --git a/Runtime/Form/WindowEntry.cs b/Runtime/Form/WindowEntry.cs new file mode 100644 index 0000000..1539592 --- /dev/null +++ b/Runtime/Form/WindowEntry.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace XericUI.Form +{ + /// + /// 窗口数据描述对象 — 由 UIFormManager 创建和持有。 + /// 描述一个窗口实例的标识信息、运行时引用、显示状态和层级关系。 + /// + public class WindowEntry + { + #region 基本标识 + + /// 唯一 ID(GUID) + public string Id { get; } + + /// 窗口标题 + public string Title; + + /// 窗口图标(任务栏和标题栏使用) + public Sprite Icon; + + /// 任务栏图标着色 + public Color TintColor = Color.white; + + #endregion + + #region 实例引用 + + /// 运行时窗口实例 + public UIFormController Controller; + + /// 窗口预制体引用 + public RectTransform Prefab; + + #endregion + + #region 显示状态 + + /// 当前窗口状态 + public WindowState State; + + /// 是否在任务栏上显示图标 + public bool ShowInTaskBar = true; + + #endregion + + #region 层级关系 + + /// 父窗口 Entry(关闭父窗口时级联关闭子窗口) + public WindowEntry ParentEntry { get; private set; } + + /// 子窗口 Entry 列表 + public readonly List ChildEntries = new List(); + + /// 添加子窗口依赖 + public void AddChild(WindowEntry child) + { + if (child == null || ChildEntries.Contains(child)) return; + child.ParentEntry = this; + ChildEntries.Add(child); + } + + /// 移除子窗口依赖 + public void RemoveChild(WindowEntry child) + { + if (child == null) return; + child.ParentEntry = null; + ChildEntries.Remove(child); + } + + #endregion + + #region 进度 + + /// 任务栏进度 (0-1) + public float Progress; + + /// 是否在任务栏显示进度条 + public bool ShowProgress; + + #endregion + + #region 构造 + + public WindowEntry(string id) + { + Id = id ?? System.Guid.NewGuid().ToString(); + } + + public WindowEntry() : this(null) { } + + #endregion + } + + #region 委托 + + /// 窗口 Entry 事件委托 + public delegate void WindowEntryEventHandler(WindowEntry entry); + + #endregion +} diff --git a/Runtime/Form/WindowEntry.cs.meta b/Runtime/Form/WindowEntry.cs.meta new file mode 100644 index 0000000..40aa404 --- /dev/null +++ b/Runtime/Form/WindowEntry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 174c44278748c9a48b2d6600a450f65a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Prefab.meta b/Runtime/Prefab.meta new file mode 100644 index 0000000..cb91269 --- /dev/null +++ b/Runtime/Prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 785b681a65a87f54e9ade90261da6d87 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Prefab/Form.meta b/Runtime/Prefab/Form.meta new file mode 100644 index 0000000..b288ccb --- /dev/null +++ b/Runtime/Prefab/Form.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7f23b52396c0c840a4f927a3468a0c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Prefab/Form/Window Title.prefab b/Runtime/Prefab/Form/Window Title.prefab new file mode 100644 index 0000000..bc565c5 --- /dev/null +++ b/Runtime/Prefab/Form/Window Title.prefab @@ -0,0 +1,1116 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &614364816953327696 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1870072387805421849} + - component: {fileID: 477255177890062157} + - component: {fileID: 5082803898349498006} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1870072387805421849 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 614364816953327696} + 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: 5104909081225547168} + m_RootOrder: 1 + 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: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &477255177890062157 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 614364816953327696} + m_CullTransparentMesh: 1 +--- !u!114 &5082803898349498006 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 614364816953327696} + 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: 1} + 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: 21300000, guid: a25c9ab25f90433458e244e933003acf, type: 3} + 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: 10 +--- !u!1 &620690997739468670 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 450102652151164027} + - component: {fileID: 6056125067687973009} + - component: {fileID: 8110723934686815148} + m_Layer: 5 + m_Name: "\u7A97\u53E3\u5316" + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &450102652151164027 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 620690997739468670} + 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: 5104909081225547168} + m_Father: {fileID: 5013155818891949195} + m_RootOrder: 1 + 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 &6056125067687973009 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 620690997739468670} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, 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: 0} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 0.5019608} + 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: 5670686958620600197} + toggleTransition: 1 + graphic: {fileID: 5082803898349498006} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: [] + m_IsOn: 0 +--- !u!222 &8110723934686815148 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 620690997739468670} + m_CullTransparentMesh: 1 +--- !u!1 &3497872670259324617 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3818289831396923672} + - component: {fileID: 9119487109894987971} + - component: {fileID: 5330696047603728753} + m_Layer: 5 + m_Name: Icon + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3818289831396923672 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3497872670259324617} + 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: 2144800616648419082} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 18, y: 0} + m_SizeDelta: {x: 30, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &9119487109894987971 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3497872670259324617} + m_CullTransparentMesh: 1 +--- !u!114 &5330696047603728753 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3497872670259324617} + 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: 1} + 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: 21300000, guid: 20c77f777936af142a12f6ea92c70161, type: 3} + 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!1 &3677843437910568646 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8232264927106219026} + - component: {fileID: 6080311456184651865} + - component: {fileID: 8119625050836726749} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8232264927106219026 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3677843437910568646} + 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: 7882577829573674029} + m_RootOrder: 0 + 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: 0, y: -5} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6080311456184651865 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3677843437910568646} + m_CullTransparentMesh: 1 +--- !u!114 &8119625050836726749 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3677843437910568646} + 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: 1} + 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: 21300000, guid: 5d082959ce575d240bb665b1ec42d749, type: 3} + 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!1 &3742107610793422745 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5104909081225547168} + - component: {fileID: 7722905847236453224} + - component: {fileID: 5670686958620600197} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5104909081225547168 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3742107610793422745} + 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: 6750121449181033628} + - {fileID: 1870072387805421849} + m_Father: {fileID: 450102652151164027} + m_RootOrder: 0 + 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!222 &7722905847236453224 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3742107610793422745} + m_CullTransparentMesh: 1 +--- !u!114 &5670686958620600197 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3742107610793422745} + 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: 1} + 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: 10 +--- !u!1 &3958647350117642535 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2144800616648419082} + - component: {fileID: 6665024858261367530} + - component: {fileID: 3780672247442027155} + - component: {fileID: 8882792753637816074} + m_Layer: 5 + m_Name: Window Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2144800616648419082 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3958647350117642535} + 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: 3818289831396923672} + - {fileID: 8799390510342556130} + - {fileID: 5013155818891949195} + m_Father: {fileID: 0} + m_RootOrder: 0 + 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: 305.6317, y: -134.9267} + m_SizeDelta: {x: 897.2634, y: 36} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &6665024858261367530 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3958647350117642535} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f5e6e4624592b3248a4c65c33cd21d5e, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IconImage: {fileID: 0} + m_TitleText: {fileID: 4632247821145074539} + m_CloseButton: {fileID: 1814331676489454388} + m_MaximizeToggle: {fileID: 0} + m_MinimizeButton: {fileID: 0} + m_AutoSnapToggle: {fileID: 0} + m_AutoHideToggle: {fileID: 0} +--- !u!222 &3780672247442027155 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3958647350117642535} + m_CullTransparentMesh: 1 +--- !u!114 &8882792753637816074 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3958647350117642535} + 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.25184909, g: 0.53985643, b: 0.8867924, a: 1} + 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!1 &4799328019211172942 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6508353797821310857} + - component: {fileID: 5233788064537585813} + - component: {fileID: 1756935852519786291} + - component: {fileID: 1814331676489454388} + m_Layer: 5 + m_Name: "\u5173\u95ED" + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6508353797821310857 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4799328019211172942} + 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: 6166662553715593466} + m_Father: {fileID: 5013155818891949195} + m_RootOrder: 2 + 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!222 &5233788064537585813 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4799328019211172942} + m_CullTransparentMesh: 1 +--- !u!114 &1756935852519786291 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4799328019211172942} + 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: 1} + 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: 1 + 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!114 &1814331676489454388 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4799328019211172942} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8bdce41790b144b78cce0334f2df1e13, 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: 0, b: 0, a: 0} + m_HighlightedColor: {r: 0.9607843, g: 0, b: 0, a: 0.5019608} + m_PressedColor: {r: 0.78431374, g: 0, b: 0, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0, b: 0, a: 1} + m_DisabledColor: {r: 0.5, g: 0, b: 0, 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: 1756935852519786291} + m_OnClick: + m_PersistentCalls: + m_Calls: [] + m_text: {fileID: 0} + m_enableTextColorTransition: 0 + m_textTransitionColorMode: + 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_enableTextContextTransition: 0 + m_textTransitionContextMode: + m_NormalTrigger: button_normal + m_HighlightedTrigger: button_highlight + m_PressedTrigger: button_pressed + m_SelectedTrigger: button_selected + m_DisabledTrigger: button_disabled +--- !u!1 &5488487651960378444 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8799390510342556130} + - component: {fileID: 6657971757996795133} + - component: {fileID: 4632247821145074539} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8799390510342556130 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5488487651960378444} + 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: 2144800616648419082} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 50, y: 0} + m_SizeDelta: {x: -230, y: 0} + m_Pivot: {x: 0, y: 0.5} +--- !u!222 &6657971757996795133 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5488487651960378444} + m_CullTransparentMesh: 1 +--- !u!114 &4632247821145074539 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5488487651960378444} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Window + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: a8c0a683785f3ae4c94b7a2cffb02078, type: 2} + m_sharedMaterial: {fileID: 8804854241636562321, guid: a8c0a683785f3ae4c94b7a2cffb02078, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 20 + m_fontSizeBase: 20 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 0 + m_wordWrappingRatios: 0.4 + m_overflowMode: 1 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &6195623315818175959 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5013155818891949195} + - component: {fileID: 251247678512295731} + m_Layer: 5 + m_Name: Group + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5013155818891949195 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6195623315818175959} + 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: 7882577829573674029} + - {fileID: 450102652151164027} + - {fileID: 6508353797821310857} + m_Father: {fileID: 2144800616648419082} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 180, y: 0} + m_Pivot: {x: 1, y: 0.5} +--- !u!114 &251247678512295731 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6195623315818175959} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 4 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &6384657472823953773 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6750121449181033628} + - component: {fileID: 5754381382532448150} + - component: {fileID: 3103925096648609860} + m_Layer: 5 + m_Name: background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6750121449181033628 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6384657472823953773} + 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: 5104909081225547168} + m_RootOrder: 0 + 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: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5754381382532448150 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6384657472823953773} + m_CullTransparentMesh: 1 +--- !u!114 &3103925096648609860 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6384657472823953773} + 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: 1} + 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: 21300000, guid: 2c6de31d1cb551b4d92a47d7de33d6c1, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 10 +--- !u!1 &7200429100440025092 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7882577829573674029} + - component: {fileID: 4466830931248569459} + - component: {fileID: 1817156327892665258} + - component: {fileID: 2789864834306041896} + m_Layer: 5 + m_Name: "\u6700\u5C0F\u5316" + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7882577829573674029 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7200429100440025092} + 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: 8232264927106219026} + m_Father: {fileID: 5013155818891949195} + m_RootOrder: 0 + 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!222 &4466830931248569459 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7200429100440025092} + m_CullTransparentMesh: 1 +--- !u!114 &1817156327892665258 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7200429100440025092} + 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: 1} + 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: 1 + 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!114 &2789864834306041896 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7200429100440025092} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, 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: 0} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 0.5019608} + 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: 1817156327892665258} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &8168484735676085777 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6166662553715593466} + - component: {fileID: 29321676436556042} + - component: {fileID: 4201270612912276207} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6166662553715593466 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8168484735676085777} + 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: 6508353797821310857} + m_RootOrder: 0 + 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: 0, y: 0} + m_SizeDelta: {x: 24, y: 24} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &29321676436556042 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8168484735676085777} + m_CullTransparentMesh: 1 +--- !u!114 &4201270612912276207 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8168484735676085777} + 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: 1} + 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: 21300000, guid: cbade0bcfd572a94298ddc62f6bada7a, type: 3} + 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 diff --git a/Runtime/Prefab/Form/Window Title.prefab.meta b/Runtime/Prefab/Form/Window Title.prefab.meta new file mode 100644 index 0000000..8959e5e --- /dev/null +++ b/Runtime/Prefab/Form/Window Title.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f1ad7693cfe6cf3448535dce51381df1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: