261 lines
7.1 KiB
C#
261 lines
7.1 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.EventSystems;
|
||
#if INPUTSYSTEM_SUPPORT
|
||
using UnityEngine.InputSystem;
|
||
#endif
|
||
#if UNITY_EDITOR
|
||
using UnityEditor;
|
||
#endif
|
||
|
||
namespace UnityEngine.UI
|
||
{
|
||
[AddComponentMenu("UI/UXToggle", 30)]
|
||
[RequireComponent(typeof(RectTransform))]
|
||
public class UXToggle : UXSelectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
|
||
#if INPUTSYSTEM_SUPPORT
|
||
, IHotkeyTrigger
|
||
#endif
|
||
{
|
||
[Serializable]
|
||
public class ToggleEvent : UnityEvent<bool> { }
|
||
|
||
public Toggle.ToggleTransition toggleTransition = Toggle.ToggleTransition.Fade;
|
||
public Graphic graphic;
|
||
|
||
[SerializeField] private UXGroup m_Group;
|
||
public UXGroup group
|
||
{
|
||
get { return m_Group; }
|
||
set
|
||
{
|
||
SetToggleGroup(value, true);
|
||
PlayEffect(true);
|
||
}
|
||
}
|
||
|
||
public ToggleEvent onValueChanged = new ToggleEvent();
|
||
|
||
[Tooltip("Is the toggle currently on or off?")]
|
||
[SerializeField] private bool m_IsOn;
|
||
|
||
protected UXToggle() { }
|
||
|
||
#if UNITY_EDITOR
|
||
protected override void OnValidate()
|
||
{
|
||
base.OnValidate();
|
||
|
||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
|
||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||
}
|
||
#endif
|
||
|
||
public virtual void Rebuild(CanvasUpdate executing)
|
||
{
|
||
#if UNITY_EDITOR
|
||
if (executing == CanvasUpdate.Prelayout)
|
||
onValueChanged.Invoke(m_IsOn);
|
||
#endif
|
||
}
|
||
|
||
public virtual void LayoutComplete() { }
|
||
public virtual void GraphicUpdateComplete() { }
|
||
|
||
protected override void OnDestroy()
|
||
{
|
||
if (m_Group != null)
|
||
m_Group.EnsureValidState();
|
||
base.OnDestroy();
|
||
}
|
||
|
||
protected override void OnEnable()
|
||
{
|
||
base.OnEnable();
|
||
PlayEffect(true);
|
||
}
|
||
|
||
protected override void OnDidApplyAnimationProperties()
|
||
{
|
||
if (graphic != null)
|
||
{
|
||
bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
|
||
if (m_IsOn != oldValue)
|
||
{
|
||
m_IsOn = oldValue;
|
||
Set(!oldValue);
|
||
}
|
||
}
|
||
|
||
base.OnDidApplyAnimationProperties();
|
||
}
|
||
|
||
// Centralized group setter logic.
|
||
private void SetToggleGroup(UXGroup newGroup, bool setMemberValue)
|
||
{
|
||
// 如果组没有改变,仍然需要确保组里包含此 toggle(修复编辑器中批量拖拽只注册最后一项的问题)
|
||
if (m_Group == newGroup)
|
||
{
|
||
if (setMemberValue)
|
||
m_Group = newGroup;
|
||
|
||
if (newGroup != null && !newGroup.ContainsToggle(this))
|
||
{
|
||
newGroup.RegisterToggle(this);
|
||
}
|
||
|
||
// 尝试同步组状态,确保编辑器批量赋值时能稳定显示
|
||
if (newGroup != null)
|
||
newGroup.EnsureValidState();
|
||
|
||
return;
|
||
}
|
||
|
||
// 从旧组注销(如果存在)
|
||
if (m_Group != null)
|
||
m_Group.UnregisterToggle(this);
|
||
|
||
if (setMemberValue)
|
||
m_Group = newGroup;
|
||
|
||
// 注册到新组(不再强依赖 IsActive(),以保证编辑器批量赋值时也能正确注册)
|
||
if (newGroup != null)
|
||
{
|
||
if (!newGroup.ContainsToggle(this))
|
||
{
|
||
newGroup.RegisterToggle(this);
|
||
}
|
||
|
||
// 如果正在 on,通知组(维持单选逻辑)
|
||
if (isOn)
|
||
newGroup.NotifyToggleOn(this);
|
||
|
||
// 同步组的内部状态,确保 Inspector 列表正确显示
|
||
newGroup.EnsureValidState();
|
||
}
|
||
}
|
||
|
||
public bool isOn
|
||
{
|
||
get { return m_IsOn; }
|
||
set { Set(value); }
|
||
}
|
||
|
||
public void SetIsOnWithoutNotify(bool value)
|
||
{
|
||
Set(value, false);
|
||
}
|
||
|
||
void Set(bool value, bool sendCallback = true)
|
||
{
|
||
if (m_IsOn == value)
|
||
return;
|
||
|
||
m_IsOn = value;
|
||
if (m_Group != null && m_Group.isActiveAndEnabled && IsActive())
|
||
{
|
||
if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.allowSwitchOff))
|
||
{
|
||
m_IsOn = true;
|
||
m_Group.NotifyToggleOn(this, sendCallback);
|
||
}
|
||
}
|
||
|
||
PlayEffect(toggleTransition == Toggle.ToggleTransition.None);
|
||
if (sendCallback)
|
||
{
|
||
UISystemProfilerApi.AddMarker("Toggle.value", this);
|
||
onValueChanged.Invoke(m_IsOn);
|
||
}
|
||
}
|
||
|
||
private void PlayEffect(bool instant)
|
||
{
|
||
if (graphic == null)
|
||
return;
|
||
|
||
#if UNITY_EDITOR
|
||
if (!Application.isPlaying)
|
||
graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
|
||
else
|
||
#endif
|
||
graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
|
||
}
|
||
|
||
protected override void Start()
|
||
{
|
||
PlayEffect(true);
|
||
}
|
||
|
||
private void InternalToggle()
|
||
{
|
||
if (!IsActive() || !IsInteractable())
|
||
return;
|
||
|
||
isOn = !isOn;
|
||
}
|
||
|
||
public override void OnPointerEnter(PointerEventData eventData)
|
||
{
|
||
base.OnPointerEnter(eventData);
|
||
PlayAudio(hoverAudioClip);
|
||
}
|
||
|
||
public virtual void OnPointerClick(PointerEventData eventData)
|
||
{
|
||
if (eventData.button != PointerEventData.InputButton.Left)
|
||
return;
|
||
|
||
InternalToggle();
|
||
PlayAudio(clickAudioClip);
|
||
}
|
||
|
||
public virtual void OnSubmit(BaseEventData eventData)
|
||
{
|
||
InternalToggle();
|
||
PlayAudio(clickAudioClip);
|
||
}
|
||
|
||
private void PlayAudio(AudioClip clip)
|
||
{
|
||
if (clip && UXComponentExtensionsHelper.AudioHelper != null)
|
||
UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
|
||
}
|
||
|
||
#if INPUTSYSTEM_SUPPORT
|
||
|
||
InputActionReference IHotkeyTrigger.HotkeyAction
|
||
{
|
||
get => _hotkeyAction;
|
||
set => _hotkeyAction = value;
|
||
}
|
||
|
||
EHotkeyPressType IHotkeyTrigger.HotkeyPressType
|
||
{
|
||
get => _hotkeyPressType;
|
||
set => _hotkeyPressType = value;
|
||
}
|
||
|
||
void IHotkeyTrigger.HotkeyActionTrigger()
|
||
{
|
||
if (interactable)
|
||
{
|
||
OnSubmit(null);
|
||
}
|
||
}
|
||
|
||
[SerializeField] internal InputActionReference _hotkeyAction;
|
||
[SerializeField] internal EHotkeyPressType _hotkeyPressType;
|
||
|
||
public InputActionReference HotKeyRefrence
|
||
{
|
||
get { return _hotkeyAction; }
|
||
}
|
||
#endif
|
||
|
||
[SerializeField] private AudioClip hoverAudioClip;
|
||
[SerializeField] private AudioClip clickAudioClip;
|
||
}
|
||
}
|