using UnityEngine; using UnityEngine.UI; using XericLibrary.Runtime.SuperStyleSheet; using XericUI.Core.StyleSheetUI; using XericUI.Helper; namespace XericUI.OverrideUI { /// /// 基于 SuperStyleSheet 的图像组件。 /// 使用 StyleField 引用样式表中的颜色和纹理路径,样式变更时自动刷新。 /// [AddComponentMenu("Xeric UI Vessel/UI/Style Image", 53)] public class XericStyleImage : Image { #region 序列化字段 [SerializeField] [Tooltip("颜色样式路径")] private StyleField m_ColorStyle = new StyleField("image/ColorTint/NormalColor"); [SerializeField] [Tooltip("纹理/精灵样式路径")] private StyleField m_TextureStyle = new StyleField("image/ColorTint/Sprite"); [SerializeField] [Tooltip("材质样式路径")] private StyleField m_MaterialStyle = new StyleField("image/ColorTint/Material"); [SerializeField] [Tooltip("启用样式表自动刷新")] private bool m_AutoRefresh = true; #endregion #region 运行时 [System.NonSerialized] private Sprite m_ResolvedSprite; #endregion #region 生命周期 protected override void OnEnable() { base.OnEnable(); if (!m_AutoRefresh) return; RegisterIfValid(m_ColorStyle); RegisterIfValid(m_TextureStyle); RegisterIfValid(m_MaterialStyle); ApplyAllStyles(); } #if UNITY_EDITOR /// 编辑器下属性变更时即刻刷新(路径从空变为有效等场景) protected override void OnValidate() { base.OnValidate(); if (!m_AutoRefresh) return; RegisterIfValid(m_ColorStyle); RegisterIfValid(m_TextureStyle); RegisterIfValid(m_MaterialStyle); ApplyAllStyles(); } #endif protected override void OnDisable() { UnregisterIfValid(m_ColorStyle); UnregisterIfValid(m_TextureStyle); UnregisterIfValid(m_MaterialStyle); base.OnDisable(); } private void RegisterIfValid(StyleField field) { if (field == null) return; // 路径为空说明未在 Inspector 中配置,跳过注册避免空路径查询错误 if (string.IsNullOrEmpty(field.StylePath)) return; // 先退订再注册,防止 OnValidate 重复调用导致双重订阅 field.Member.OnValueChanged -= OnStyleChanged; field.Register(); field.Member.OnValueChanged += OnStyleChanged; } private void UnregisterIfValid(StyleField field) { if (field == null) return; if (string.IsNullOrEmpty(field.StylePath)) return; field.Member.OnValueChanged -= OnStyleChanged; field.Unregister(); } #endregion #region 样式应用 public void ApplyAllStyles() { if (m_ColorStyle != null) { StyleValue val = m_ColorStyle.GetValue(); if (val != null) color = val; } if (m_TextureStyle != null) { StyleValue val = m_TextureStyle.GetValue(); if (val != null) { // 通过 Unity Object 基类获取并类型判断,抑制 Color→Object 转换的日志错误 Object obj = SafeGetObject(val); if (obj is Sprite sp) { m_ResolvedSprite = sp; sprite = sp; } else if (obj is Texture2D tex) { m_ResolvedSprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); sprite = m_ResolvedSprite; } } } if (m_MaterialStyle != null) { StyleValue val = m_MaterialStyle.GetValue(); if (val != null) { Object obj = SafeGetObject(val); if (obj is Material mat) material = mat; } } } private void OnStyleChanged(StyleValue val) { if (gameObject.activeInHierarchy) ApplyAllStyles(); } #endregion #region 辅助 /// /// 安全获取 UnityEngine.Object 类型值——临时关闭日志以抑制 StyleValue 内部的类型不匹配错误。 /// private static T SafeGetObject(StyleValue val) where T : Object { if (val == null) return null; var logger = Debug.unityLogger; bool prev = logger.logEnabled; logger.logEnabled = false; try { return val.GetValueAs(); } finally { logger.logEnabled = prev; } } #endregion #region 组件替换 #if UNITY_EDITOR [UnityEditor.MenuItem("CONTEXT/Image/Replace To/Xeric (Style)", true)] private static bool ValidateReplaceToStyleImage(UnityEditor.MenuCommand command) => command.context is Image and not XericStyleImage; [UnityEditor.MenuItem("CONTEXT/Image/Replace To/Xeric (Style)", false, 11)] private static void ReplaceToStyleImage(UnityEditor.MenuCommand command) => command.ReplaceCommandContext( o => o.sprite, (t, d) => { t.sprite = d; }); [UnityEditor.MenuItem("CONTEXT/Image/Replace To/Unity Image (Style)", true)] private static bool ValidateReplaceStyleImageToUnity(UnityEditor.MenuCommand command) => command.context is XericStyleImage; [UnityEditor.MenuItem("CONTEXT/Image/Replace To/Unity Image (Style)", false, 100)] private static void ReplaceStyleImageToUnity(UnityEditor.MenuCommand command) => command.ReplaceCommandContext( o => o.sprite, (t, d) => { t.sprite = d; }); #endif #endregion } }