Files
2026-07-09 14:05:38 +08:00

271 lines
8.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.UI;
using XericLibrary.Runtime.SuperStyleSheet;
using XericUI.Core.StyleSheetUI;
using XericUI.Helper;
namespace XericUI.OverrideUI
{
/// <summary>
/// 基于 SuperStyleSheet 的开关组件。
/// 通过 TransitionMode 选择颜色切换、纹理切换或两者并用,
/// 额外支持 isOn 覆盖和 Checkmark 样式。
/// </summary>
[AddComponentMenu("Xeric UI Vessel/UI/Style Toggle", 52)]
public class XericStyleToggle : Toggle, IStyleRefreshable
{
/// <summary>样式过渡模式</summary>
public enum TransitionMode
{
/// <summary>仅颜色切换</summary>
Color,
/// <summary>仅纹理/Sprite 切换</summary>
Sprite,
/// <summary>颜色 + 纹理切换</summary>
ColorAndSprite
}
#region 序列化字段
[Header("过渡模式")]
[SerializeField]
[Tooltip("选择颜色 / 纹理 / 同时切换")]
private TransitionMode m_TransitionMode = TransitionMode.ColorAndSprite;
[Header("样式表状态")]
[SerializeField]
[Tooltip("五态颜色样式块(ColorTint")]
private StyleSheetState m_ColorState = new StyleSheetState("toggle", Transition.ColorTint);
[SerializeField]
[Tooltip("五态纹理样式块(SpriteState")]
private StyleSheetState m_TextureState = new StyleSheetState("toggle", Transition.SpriteSwap);
[Header("Toggle 专用")]
[SerializeField]
[Tooltip("isOn=true 时的颜色覆盖")]
private StyleField m_OnOverrideColor = new StyleField();
[SerializeField]
[Tooltip("isOn=true 时的纹理覆盖")]
private StyleField m_OnOverrideTexture = new StyleField();
[SerializeField]
[Tooltip("Checkmark 打勾标志的样式")]
private StyleField m_CheckmarkStyle = new StyleField();
[SerializeField]
[Tooltip("启用样式表驱动过渡")]
private bool m_EnableStyleTransition = true;
#endregion
#region 运行时
[System.NonSerialized] private StyleSheetSelectionState m_CurrentState = StyleSheetSelectionState.Normal;
[System.NonSerialized] private Graphic m_TargetGraphic;
[System.NonSerialized] private Image m_TargetImage;
[System.NonSerialized] private Graphic m_CheckmarkGraphic;
[System.NonSerialized] private Image m_CheckmarkImage;
#endregion
#region 生命周期
protected override void Awake()
{
base.Awake();
m_TargetGraphic = targetGraphic;
m_TargetImage = m_TargetGraphic as Image;
Transform checkmark = transform.Find("Background/Checkmark")
?? transform.Find("Checkmark");
if (checkmark != null)
{
m_CheckmarkGraphic = checkmark.GetComponent<Graphic>();
m_CheckmarkImage = checkmark.GetComponent<Image>();
}
}
protected override void OnEnable()
{
base.OnEnable();
RevalidateStyles();
StyleRefreshManager.Register(this);
onValueChanged.AddListener(OnToggleValueChanged);
ApplyForState(m_CurrentState);
}
#if UNITY_EDITOR
/// <summary>编辑器下属性变更时即刻刷新(路径从空变为有效等场景)</summary>
protected override void OnValidate()
{
base.OnValidate();
RevalidateStyles();
ApplyForState(m_CurrentState);
}
#endif
/// <summary>重新注册/订阅所有 StyleField(供 OnEnable 和 OnValidate 共用)</summary>
private void RevalidateStyles()
{
// 先退订防止双重订阅
m_ColorState?.UnsubscribeAll(OnStyleChanged);
m_TextureState?.UnsubscribeAll(OnStyleChanged);
m_OnOverrideColor.Member.OnValueChanged -= OnStyleChanged;
m_OnOverrideTexture.Member.OnValueChanged -= OnStyleChanged;
m_CheckmarkStyle.Member.OnValueChanged -= OnStyleChanged;
m_ColorState?.RegisterAll();
m_TextureState?.RegisterAll();
m_ColorState?.SubscribeAll(OnStyleChanged);
m_TextureState?.SubscribeAll(OnStyleChanged);
m_OnOverrideColor?.Register();
m_OnOverrideColor.Member.OnValueChanged += OnStyleChanged;
m_OnOverrideTexture?.Register();
m_OnOverrideTexture.Member.OnValueChanged += OnStyleChanged;
m_CheckmarkStyle?.Register();
m_CheckmarkStyle.Member.OnValueChanged += OnStyleChanged;
}
protected override void OnDisable()
{
StyleRefreshManager.Unregister(this);
onValueChanged.RemoveListener(OnToggleValueChanged);
m_ColorState?.UnsubscribeAll(OnStyleChanged);
m_TextureState?.UnsubscribeAll(OnStyleChanged);
m_ColorState?.UnregisterAll();
m_TextureState?.UnregisterAll();
if (m_OnOverrideColor != null) { m_OnOverrideColor.Member.OnValueChanged -= OnStyleChanged; m_OnOverrideColor.Unregister(); }
if (m_OnOverrideTexture != null) { m_OnOverrideTexture.Member.OnValueChanged -= OnStyleChanged; m_OnOverrideTexture.Unregister(); }
if (m_CheckmarkStyle != null) { m_CheckmarkStyle.Member.OnValueChanged -= OnStyleChanged; m_CheckmarkStyle.Unregister(); }
base.OnDisable();
}
#endregion
#region 状态过渡
protected override void DoStateTransition(SelectionState state, bool instant)
{
if (!gameObject.activeInHierarchy) return;
m_CurrentState = (StyleSheetSelectionState)(int)state;
if (m_EnableStyleTransition)
ApplyForState(m_CurrentState, instant);
else
base.DoStateTransition(state, instant);
}
private void OnToggleValueChanged(bool _)
{
if (m_EnableStyleTransition)
ApplyForState(m_CurrentState);
}
#endregion
#region 样式应用
public void RefreshStyle()
{
ApplyForState(m_CurrentState, false);
}
private void ApplyForState(StyleSheetSelectionState state, bool instant = false)
{
if (m_TransitionMode == TransitionMode.Color || m_TransitionMode == TransitionMode.ColorAndSprite)
{
// 颜色:On 覆盖优先
StyleField colorField = isOn && m_OnOverrideColor?.GetValue() != null
? m_OnOverrideColor
: m_ColorState?.GetForState(state);
if (colorField != null && m_TargetGraphic != null)
{
StyleValue val = colorField.GetValue();
if (val != null)
{
float dur = instant ? 0f : (m_ColorState?.FadeDuration ?? 0.1f);
m_TargetGraphic.CrossFadeColor((Color)val, dur, true, true);
}
}
}
if (m_TransitionMode == TransitionMode.Sprite || m_TransitionMode == TransitionMode.ColorAndSprite)
{
// 纹理:On 覆盖优先
StyleField texField = isOn && m_OnOverrideTexture?.GetValue() != null
? m_OnOverrideTexture
: m_TextureState?.GetForState(state);
if (texField != null && m_TargetImage != null)
{
StyleValue val = texField.GetValue();
Texture2D tex = val?.GetValueAs<Texture2D>();
if (tex != null)
m_TargetImage.overrideSprite = Sprite.Create(tex,
new Rect(0, 0, tex.width, tex.height),
new Vector2(0.5f, 0.5f));
}
}
// Checkmark(独立于 TransitionMode
if (m_CheckmarkGraphic != null && m_CheckmarkStyle != null)
{
StyleValue val = m_CheckmarkStyle.GetValue();
if (val != null) m_CheckmarkGraphic.color = val;
}
}
private void OnStyleChanged(StyleValue _)
{
if (gameObject.activeInHierarchy)
ApplyForState(m_CurrentState);
}
#endregion
#region 组件替换
#if UNITY_EDITOR
[UnityEditor.MenuItem("CONTEXT/XericStyleToggle/Xeric UI/强制刷新样式", false, 1)]
private static void ForceRefreshStyle(UnityEditor.MenuCommand command)
{
StyleRefreshManager.ForceRefreshAll();
}
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To/Xeric (Style)", true)]
private static bool ValidateReplaceToStyleToggle(UnityEditor.MenuCommand command)
=> command.context is Toggle and not XericStyleToggle;
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To/Xeric (Style)", false, 11)]
private static void ReplaceToStyleToggle(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext<Toggle, XericStyleToggle,
(Graphic graphic, ToggleGroup group, ToggleEvent onValueChanged, Navigation navigation)>(
o => (o.graphic, o.group, o.onValueChanged, o.navigation),
(t, d) => { t.graphic = d.graphic; t.group = d.group; t.onValueChanged = d.onValueChanged; t.navigation = d.navigation; });
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To/Unity Toggle (Style)", true)]
private static bool ValidateReplaceStyleToggleToUnity(UnityEditor.MenuCommand command)
=> command.context is XericStyleToggle;
[UnityEditor.MenuItem("CONTEXT/Toggle/Replace To/Unity Toggle (Style)", false, 100)]
private static void ReplaceStyleToggleToUnity(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext<XericStyleToggle, Toggle,
(Graphic graphic, ToggleGroup group, ToggleEvent onValueChanged, Navigation navigation)>(
o => (o.graphic, o.group, o.onValueChanged, o.navigation),
(t, d) => { t.graphic = d.graphic; t.group = d.group; t.onValueChanged = d.onValueChanged; t.navigation = d.navigation; });
#endif
#endregion
}
}