using System; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.Serialization; namespace UnityEngine.UI { /// /// A standard toggle that has an on / off state. /// /// /// The toggle component is a Selectable that controls a child graphic which displays the on / off state. /// When a toggle event occurs a callback is sent to any registered listeners of UI.Toggle._onValueChanged. /// [AddComponentMenu("UI/Toggle", 30)] [RequireComponent(typeof(RectTransform))] public class Toggle : Selectable, IPointerClickHandler, ISubmitHandler, ICanvasElement { /// /// Display settings for when a toggle is activated or deactivated. /// public enum ToggleTransition { /// /// Show / hide the toggle instantly /// None, /// /// Fade the toggle in / out smoothly. /// Fade } [Serializable] /// /// UnityEvent callback for when a toggle is toggled. /// public class ToggleEvent : UnityEvent {} /// /// Transition mode for the toggle. /// public ToggleTransition toggleTransition = ToggleTransition.Fade; /// /// Graphic the toggle should be working with. /// public Graphic graphic; [SerializeField] private ToggleGroup m_Group; /// /// Group the toggle belongs to. /// public ToggleGroup group { get { return m_Group; } set { SetToggleGroup(value, true); PlayEffect(true); } } /// /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers. /// /// /// /// UI>Toggle. /// //Set your own Text in the Inspector window /// /// using UnityEngine; /// using UnityEngine.UI; /// /// public class Example : MonoBehaviour /// { /// Toggle m_Toggle; /// public Text m_Text; /// /// void Start() /// { /// //Fetch the Toggle GameObject /// m_Toggle = GetComponent(); /// //Add listener for when the state of the Toggle changes, to take action /// m_Toggle.onValueChanged.AddListener(delegate { /// ToggleValueChanged(m_Toggle); /// }); /// /// //Initialise the Text to say the first state of the Toggle /// m_Text.text = "First Value : " + m_Toggle.isOn; /// } /// /// //Output the new state of the Toggle into Text /// void ToggleValueChanged(Toggle change) /// { /// m_Text.text = "New Value : " + m_Toggle.isOn; /// } /// } /// ]]> /// /// public ToggleEvent onValueChanged = new ToggleEvent(); // Whether the toggle is on [Tooltip("Is the toggle currently on or off?")] [SerializeField] private bool m_IsOn; protected Toggle() {} #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying) CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); } #endif // if UNITY_EDITOR 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(); SetToggleGroup(m_Group, false); PlayEffect(true); } protected override void OnDisable() { SetToggleGroup(null, false); base.OnDisable(); } protected override void OnDidApplyAnimationProperties() { // Check if isOn has been changed by the animation. // Unfortunately there is no way to check if we don�t have a graphic. if (graphic != null) { bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0); if (m_IsOn != oldValue) { m_IsOn = oldValue; Set(!oldValue); } } base.OnDidApplyAnimationProperties(); } private void SetToggleGroup(ToggleGroup newGroup, bool setMemberValue) { // Sometimes IsActive returns false in OnDisable so don't check for it. // Rather remove the toggle too often than too little. if (m_Group != null) m_Group.UnregisterToggle(this); // At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable. // That's why we use the setMemberValue parameter. if (setMemberValue) m_Group = newGroup; // Only register to the new group if this Toggle is active. if (newGroup != null && IsActive()) newGroup.RegisterToggle(this); // If we are in a new group, and this toggle is on, notify group. // Note: Don't refer to m_Group here as it's not guaranteed to have been set. if (newGroup != null && isOn && IsActive()) newGroup.NotifyToggleOn(this); } /// /// Whether the toggle is currently active. /// /// /// /// UI>Toggle. /// //Set your own Text in the Inspector window /// /// using UnityEngine; /// using UnityEngine.UI; /// /// public class Example : MonoBehaviour /// { /// Toggle m_Toggle; /// public Text m_Text; /// /// void Start() /// { /// //Fetch the Toggle GameObject /// m_Toggle = GetComponent(); /// //Add listener for when the state of the Toggle changes, and output the state /// m_Toggle.onValueChanged.AddListener(delegate { /// ToggleValueChanged(m_Toggle); /// }); /// /// //Initialize the Text to say whether the Toggle is in a positive or negative state /// m_Text.text = "Toggle is : " + m_Toggle.isOn; /// } /// /// //Output the new state of the Toggle into Text when the user uses the Toggle /// void ToggleValueChanged(Toggle change) /// { /// m_Text.text = "Toggle is : " + m_Toggle.isOn; /// } /// } /// ]]> /// /// public bool isOn { get { return m_IsOn; } set { Set(value); } } /// /// Set isOn without invoking onValueChanged callback. /// /// New Value for isOn. public void SetIsOnWithoutNotify(bool value) { Set(value, false); } void Set(bool value, bool sendCallback = true) { if (m_IsOn == value) return; // if we are in a group and set to true, do group logic 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); } } // Always send event when toggle is clicked, even if value didn't change // due to already active toggle in a toggle group being clicked. // Controls like Dropdown rely on this. // It's up to the user to ignore a selection being set to the same value it already was, if desired. PlayEffect(toggleTransition == ToggleTransition.None); if (sendCallback) { UISystemProfilerApi.AddMarker("Toggle.value", this); onValueChanged.Invoke(m_IsOn); } } /// /// Play the appropriate effect. /// 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); } /// /// Assume the correct visual state. /// protected override void Start() { PlayEffect(true); } private void InternalToggle() { if (!IsActive() || !IsInteractable()) return; isOn = !isOn; } /// /// React to clicks. /// public virtual void OnPointerClick(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; InternalToggle(); } public virtual void OnSubmit(BaseEventData eventData) { InternalToggle(); } } }