Toggle.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. using System;
  2. using UnityEngine.Events;
  3. using UnityEngine.EventSystems;
  4. using UnityEngine.Serialization;
  5. namespace UnityEngine.UI
  6. {
  7. /// <summary>
  8. /// A standard toggle that has an on / off state.
  9. /// </summary>
  10. /// <remarks>
  11. /// The toggle component is a Selectable that controls a child graphic which displays the on / off state.
  12. /// When a toggle event occurs a callback is sent to any registered listeners of UI.Toggle._onValueChanged.
  13. /// </remarks>
  14. [AddComponentMenu("UI/Toggle", 31)]
  15. [RequireComponent(typeof(RectTransform))]
  16. public class Toggle : Selectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
  17. {
  18. /// <summary>
  19. /// Display settings for when a toggle is activated or deactivated.
  20. /// </summary>
  21. public enum ToggleTransition
  22. {
  23. /// <summary>
  24. /// Show / hide the toggle instantly
  25. /// </summary>
  26. None,
  27. /// <summary>
  28. /// Fade the toggle in / out smoothly.
  29. /// </summary>
  30. Fade
  31. }
  32. [Serializable]
  33. /// <summary>
  34. /// UnityEvent callback for when a toggle is toggled.
  35. /// </summary>
  36. public class ToggleEvent : UnityEvent<bool>
  37. {}
  38. /// <summary>
  39. /// Transition mode for the toggle.
  40. /// </summary>
  41. public ToggleTransition toggleTransition = ToggleTransition.Fade;
  42. /// <summary>
  43. /// Graphic the toggle should be working with.
  44. /// </summary>
  45. public Graphic graphic;
  46. [SerializeField]
  47. private ToggleGroup m_Group;
  48. /// <summary>
  49. /// Group the toggle belongs to.
  50. /// </summary>
  51. public ToggleGroup group
  52. {
  53. get { return m_Group; }
  54. set
  55. {
  56. SetToggleGroup(value, true);
  57. PlayEffect(true);
  58. }
  59. }
  60. /// <summary>
  61. /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
  62. /// </summary>
  63. /// <example>
  64. /// <code>
  65. /// //Attach this script to a Toggle GameObject. To do this, go to Create>UI>Toggle.
  66. /// //Set your own Text in the Inspector window
  67. ///
  68. /// using UnityEngine;
  69. /// using UnityEngine.UI;
  70. ///
  71. /// public class Example : MonoBehaviour
  72. /// {
  73. /// Toggle m_Toggle;
  74. /// public Text m_Text;
  75. ///
  76. /// void Start()
  77. /// {
  78. /// //Fetch the Toggle GameObject
  79. /// m_Toggle = GetComponent<Toggle>();
  80. /// //Add listener for when the state of the Toggle changes, to take action
  81. /// m_Toggle.onValueChanged.AddListener(delegate {
  82. /// ToggleValueChanged(m_Toggle);
  83. /// });
  84. ///
  85. /// //Initialise the Text to say the first state of the Toggle
  86. /// m_Text.text = "First Value : " + m_Toggle.isOn;
  87. /// }
  88. ///
  89. /// //Output the new state of the Toggle into Text
  90. /// void ToggleValueChanged(Toggle change)
  91. /// {
  92. /// m_Text.text = "New Value : " + m_Toggle.isOn;
  93. /// }
  94. /// }
  95. /// </code>
  96. /// </example>
  97. public ToggleEvent onValueChanged = new ToggleEvent();
  98. // Whether the toggle is on
  99. [Tooltip("Is the toggle currently on or off?")]
  100. [SerializeField]
  101. private bool m_IsOn;
  102. protected Toggle()
  103. {}
  104. #if UNITY_EDITOR
  105. protected override void OnValidate()
  106. {
  107. base.OnValidate();
  108. if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
  109. CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
  110. }
  111. #endif // if UNITY_EDITOR
  112. public virtual void Rebuild(CanvasUpdate executing)
  113. {
  114. #if UNITY_EDITOR
  115. if (executing == CanvasUpdate.Prelayout)
  116. onValueChanged.Invoke(m_IsOn);
  117. #endif
  118. }
  119. public virtual void LayoutComplete()
  120. {}
  121. public virtual void GraphicUpdateComplete()
  122. {}
  123. protected override void OnDestroy()
  124. {
  125. if (m_Group != null)
  126. m_Group.EnsureValidState();
  127. base.OnDestroy();
  128. }
  129. protected override void OnEnable()
  130. {
  131. base.OnEnable();
  132. SetToggleGroup(m_Group, false);
  133. PlayEffect(true);
  134. }
  135. protected override void OnDisable()
  136. {
  137. SetToggleGroup(null, false);
  138. base.OnDisable();
  139. }
  140. protected override void OnDidApplyAnimationProperties()
  141. {
  142. // Check if isOn has been changed by the animation.
  143. // Unfortunately there is no way to check if we don�t have a graphic.
  144. if (graphic != null)
  145. {
  146. bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
  147. if (m_IsOn != oldValue)
  148. {
  149. m_IsOn = oldValue;
  150. Set(!oldValue);
  151. }
  152. }
  153. base.OnDidApplyAnimationProperties();
  154. }
  155. private void SetToggleGroup(ToggleGroup newGroup, bool setMemberValue)
  156. {
  157. // Sometimes IsActive returns false in OnDisable so don't check for it.
  158. // Rather remove the toggle too often than too little.
  159. if (m_Group != null)
  160. m_Group.UnregisterToggle(this);
  161. // At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable.
  162. // That's why we use the setMemberValue parameter.
  163. if (setMemberValue)
  164. m_Group = newGroup;
  165. // Only register to the new group if this Toggle is active.
  166. if (newGroup != null && IsActive())
  167. newGroup.RegisterToggle(this);
  168. // If we are in a new group, and this toggle is on, notify group.
  169. // Note: Don't refer to m_Group here as it's not guaranteed to have been set.
  170. if (newGroup != null && isOn && IsActive())
  171. newGroup.NotifyToggleOn(this);
  172. }
  173. /// <summary>
  174. /// Whether the toggle is currently active.
  175. /// </summary>
  176. /// <example>
  177. /// <code>
  178. /// /Attach this script to a Toggle GameObject. To do this, go to Create>UI>Toggle.
  179. /// //Set your own Text in the Inspector window
  180. ///
  181. /// using UnityEngine;
  182. /// using UnityEngine.UI;
  183. ///
  184. /// public class Example : MonoBehaviour
  185. /// {
  186. /// Toggle m_Toggle;
  187. /// public Text m_Text;
  188. ///
  189. /// void Start()
  190. /// {
  191. /// //Fetch the Toggle GameObject
  192. /// m_Toggle = GetComponent<Toggle>();
  193. /// //Add listener for when the state of the Toggle changes, and output the state
  194. /// m_Toggle.onValueChanged.AddListener(delegate {
  195. /// ToggleValueChanged(m_Toggle);
  196. /// });
  197. ///
  198. /// //Initialize the Text to say whether the Toggle is in a positive or negative state
  199. /// m_Text.text = "Toggle is : " + m_Toggle.isOn;
  200. /// }
  201. ///
  202. /// //Output the new state of the Toggle into Text when the user uses the Toggle
  203. /// void ToggleValueChanged(Toggle change)
  204. /// {
  205. /// m_Text.text = "Toggle is : " + m_Toggle.isOn;
  206. /// }
  207. /// }
  208. /// </code>
  209. /// </example>
  210. public bool isOn
  211. {
  212. get { return m_IsOn; }
  213. set
  214. {
  215. Set(value);
  216. }
  217. }
  218. /// <summary>
  219. /// Set isOn without invoking onValueChanged callback.
  220. /// </summary>
  221. /// <param name="value">New Value for isOn.</param>
  222. public void SetIsOnWithoutNotify(bool value)
  223. {
  224. Set(value, false);
  225. }
  226. void Set(bool value, bool sendCallback = true)
  227. {
  228. if (m_IsOn == value)
  229. return;
  230. // if we are in a group and set to true, do group logic
  231. m_IsOn = value;
  232. if (m_Group != null && m_Group.isActiveAndEnabled && IsActive())
  233. {
  234. if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.allowSwitchOff))
  235. {
  236. m_IsOn = true;
  237. m_Group.NotifyToggleOn(this, sendCallback);
  238. }
  239. }
  240. // Always send event when toggle is clicked, even if value didn't change
  241. // due to already active toggle in a toggle group being clicked.
  242. // Controls like Dropdown rely on this.
  243. // It's up to the user to ignore a selection being set to the same value it already was, if desired.
  244. PlayEffect(toggleTransition == ToggleTransition.None);
  245. if (sendCallback)
  246. {
  247. UISystemProfilerApi.AddMarker("Toggle.value", this);
  248. onValueChanged.Invoke(m_IsOn);
  249. }
  250. }
  251. /// <summary>
  252. /// Play the appropriate effect.
  253. /// </summary>
  254. private void PlayEffect(bool instant)
  255. {
  256. if (graphic == null)
  257. return;
  258. #if UNITY_EDITOR
  259. if (!Application.isPlaying)
  260. graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
  261. else
  262. #endif
  263. graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
  264. }
  265. /// <summary>
  266. /// Assume the correct visual state.
  267. /// </summary>
  268. protected override void Start()
  269. {
  270. PlayEffect(true);
  271. }
  272. private void InternalToggle()
  273. {
  274. if (!IsActive() || !IsInteractable())
  275. return;
  276. isOn = !isOn;
  277. }
  278. /// <summary>
  279. /// React to clicks.
  280. /// </summary>
  281. public virtual void OnPointerClick(PointerEventData eventData)
  282. {
  283. if (eventData.button != PointerEventData.InputButton.Left)
  284. return;
  285. InternalToggle();
  286. }
  287. public virtual void OnSubmit(BaseEventData eventData)
  288. {
  289. InternalToggle();
  290. }
  291. }
  292. }